diff --git a/src/app/registers/RegisterBrowser.tsx b/src/app/registers/RegisterBrowser.tsx index 4d74ac1..5218b0c 100644 --- a/src/app/registers/RegisterBrowser.tsx +++ b/src/app/registers/RegisterBrowser.tsx @@ -12,13 +12,14 @@ interface RegisterBrowserProps { /** * Renders the access details for a register, including its description, operations, and notes. * @param access The register access data to render. + * @param extraNotes Non access footnotes to include in the tooltip. * @returns A React component that displays the register access details. */ -export function renderAccess(access: RegisterAccess) { +export function renderAccess(access: RegisterAccess, extraNotes: Note[] = []) { const renderTooltip = (notes: Note[]) => ( {notes.map((note, index) => ( -
{note.text}
+
{note.ref} {note.text}
))}
); @@ -36,7 +37,11 @@ export function renderAccess(access: RegisterAccess) { {access.operations.map((op, index) => { - const notes = access.notes.filter(note => note.ref === op.footnoteRef); + const access_notes = access.notes.filter(note => note.ref === op.footnoteRef); + const extra_notes = extraNotes.filter(note => note.ref === op.footnoteRef); + + const notes = [...access_notes, ...extra_notes].filter(note => note.text.length > 0); + return ( {op.bits} @@ -44,7 +49,7 @@ export function renderAccess(access: RegisterAccess) { {op.description} {op.footnoteRef && notes.length > 0 && ( - {op.footnoteRef} + {op.footnoteRef} )} diff --git a/src/app/registers/RegisterDetail.tsx b/src/app/registers/RegisterDetail.tsx index b32c22a..cd6b8d2 100644 --- a/src/app/registers/RegisterDetail.tsx +++ b/src/app/registers/RegisterDetail.tsx @@ -19,6 +19,8 @@ export default function RegisterDetail({ }) { const [showSource, setShowSource] = useState(false); + console.log("RENDERING: ", register.name, "FROM", register); + return ( @@ -41,26 +43,34 @@ export default function RegisterDetail({ { register.modes.map((mode, idx) => ( -
0 ? 'mt-4' : ''}> - {register.modes.length > 1 && ( -
Mode {idx + 1}
- )} - - {mode.common && {renderAccess(mode.common)}} - {mode.read && {renderAccess(mode.read)}} - {mode.write && {renderAccess(mode.write)}} - - {mode.notes && mode.notes.map((note, index) => ( -

{note.ref} {note.text}

- ))} - {mode.text && mode.text.length > 0 && ( -
-
Notes:
-
{mode.text}
+
0 ? 'mt-4' : ''}> +
+ {mode.modeName?.length && ( +
{mode.modeName}
+ )} + + {mode.common && {renderAccess(mode.common, register.notes)}} + {mode.read && {renderAccess(mode.read, register.notes)}} + {mode.write && {renderAccess(mode.write, register.notes)}} + +
+ {mode.text && mode.text.length > 0 && ( +
+
Mode Notes:
+
{mode.text}
+
+ )}
- )} -
))} + {register.notes && register.notes.map((note, index) => ( +

{note.ref}: {note.text}

+ ))} + {register.text && register.text.length > 0 && ( +
+
Notes:
+
{register.text}
+
+ )} diff --git a/src/app/scss/_variables.scss b/src/app/scss/_variables.scss index 93f9444..0635ed1 100644 --- a/src/app/scss/_variables.scss +++ b/src/app/scss/_variables.scss @@ -1,5 +1,6 @@ // Pulse 5.3.8 // Bootswatch +@use 'sass:color'; $theme: "pulse" !default; @@ -86,7 +87,7 @@ $progress-bar-bg: $primary !default; $list-group-bg: $gray-900 !default; $list-group-border-color: transparent !default; -$list-group-hover-bg: lighten($list-group-bg, 10%) !default; +$list-group-hover-bg: color.scale($list-group-bg, $lightness:10%) !default; $list-group-active-color: $white !default; $list-group-active-bg: $list-group-bg !default; -$list-group-disabled-color: lighten($list-group-bg, 30%) !default; +$list-group-disabled-color: color.scale($list-group-bg, $lightness:30%) !default; diff --git a/src/app/services/register.service.ts b/src/app/services/register.service.ts index dfa71de..a614138 100644 --- a/src/app/services/register.service.ts +++ b/src/app/services/register.service.ts @@ -11,10 +11,10 @@ let registers: Register[] = []; * @returns A promise that resolves to an array of Register objects. */ export async function getRegisters(): Promise { - if (registers.length === 0) { + // if (registers.length === 0) { const filePath = path.join(process.cwd(), 'data', 'nextreg.txt'); const fileContent = await fs.readFile(filePath, 'utf8'); registers = await parseNextReg(fileContent); - } + // } return registers; } diff --git a/src/utils/register_parser.ts b/src/utils/register_parser.ts index da49f35..07d2406 100644 --- a/src/utils/register_parser.ts +++ b/src/utils/register_parser.ts @@ -20,11 +20,11 @@ export interface RegisterAccess { } export interface RegisterDetail { + modeName?: string; read?: RegisterAccess; write?: RegisterAccess; common?: RegisterAccess; text: string; - notes: Note[]; } export interface Register { @@ -35,6 +35,8 @@ export interface Register { description: string; issue_4_only: boolean; source: string[]; + text: string; + notes: Note[]; } /** @@ -91,7 +93,9 @@ export function processRegisterBlock(paragraph: string, registers: Register[]) { description: description, modes: [], issue_4_only: false, - source: [] + source: [], + text: "", + notes: [], }; // Dispatch to appropriate parser based on hex diff --git a/src/utils/register_parsers/reg_default.ts b/src/utils/register_parsers/reg_default.ts index 4ebb55f..938166a 100644 --- a/src/utils/register_parsers/reg_default.ts +++ b/src/utils/register_parsers/reg_default.ts @@ -5,11 +5,12 @@ export const parseDescriptionDefault = (reg: Register, description: string) => { let currentAccess: 'read' | 'write' | 'common' | null = null; let accessData: RegisterAccess = { operations: [], notes: [] }; // Prepare a new RegisterDetail for this description block - const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: '', notes: [] }; + const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: ''}; for (const line of descriptionLines) { if (line.includes('Issue 4 Only')) reg.issue_4_only = true; + const spaces_at_start = line.match(/^(\s*)/)?.[0].length || 0; const trimmedLine = line.trim(); reg.source.push(line); @@ -50,7 +51,7 @@ export const parseDescriptionDefault = (reg: Register, description: string) => { if (currentAccess) { const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); - const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); + // const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); if (bitMatch) { let bitDescription = bitMatch[3]; @@ -65,13 +66,15 @@ export const parseDescriptionDefault = (reg: Register, description: string) => { description: bitDescription, footnoteRef: footnoteRef, }); - } else if (valueMatch) { - accessData.operations.push({ - bits: valueMatch[1].trim().replace(/\s/g, ''), - description: valueMatch[2].trim(), - }); + // } else if (valueMatch) { + // console.error("VALUE MATCH",valueMatch); + // accessData.operations.push({ + // bits: valueMatch[1].trim().replace(/\s/g, ''), + // description: valueMatch[2].trim(), + // }); } else if (trimmedLine.startsWith('*')) { const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); + console.log("NOTE MATCH",noteMatch); if (noteMatch) { accessData.notes.push({ ref: noteMatch[1], @@ -79,6 +82,12 @@ export const parseDescriptionDefault = (reg: Register, description: string) => { }); } } else if (trimmedLine) { + if(spaces_at_start == 2) { + reg.text += `${line}\n`; + continue; + } + // console.log("LINE",line); + console.log(line.match(/^\s+/), line); if (line.match(/^\s+/) && accessData.operations.length > 0) { accessData.operations[accessData.operations.length - 1].description += `\n${line}`; } else { @@ -92,7 +101,7 @@ export const parseDescriptionDefault = (reg: Register, description: string) => { if (trimmedLine.startsWith('*')) { const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); if (noteMatch) { - detail.notes.push({ + reg.notes.push({ ref: noteMatch[1], text: noteMatch[2], }); diff --git a/src/utils/register_parsers/reg_f0.ts b/src/utils/register_parsers/reg_f0.ts index 1c7a68a..7d2e20b 100644 --- a/src/utils/register_parsers/reg_f0.ts +++ b/src/utils/register_parsers/reg_f0.ts @@ -1,101 +1,131 @@ -// Special-case parser for 0xF0 (XDEV CMD): treat headings beginning with '*' inside access blocks -// as descriptive text instead of notes, so sub-modes become part of the section descriptions. -import {Register, RegisterAccess, RegisterDetail} from "@/utils/register_parser"; +// Special-case parser for 0xF0 (XDEV CMD): implement multi-mode parsing. +// Rules: +// - A line that begins with exactly two spaces (" ") and then a non-* character starts a new mode; the trimmed text is modeName. +// - Lines with three or more leading spaces (>=3) belong to the current mode. +// - A line with exactly two spaces followed by '*' is a parent (register-level) note, not a mode note. +// - Inside access blocks for F0, lines starting with '*' are headings for description (not notes). +import { Register, RegisterAccess, RegisterDetail } from "@/utils/register_parser"; export const parseDescriptionF0 = (reg: Register, description: string) => { - const descriptionLines = description.split('\n'); - let currentAccess: 'read' | 'write' | 'common' | null = null; - let accessData: RegisterAccess = { operations: [], notes: [] }; - const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: '', notes: [] }; + const descriptionLines = description.split('\n'); + let currentAccess: 'read' | 'write' | 'common' | null = null; + let accessData: RegisterAccess = { operations: [], notes: [] }; + // Prepare a new RegisterDetail for this description block + let detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: ''}; + reg.modes = reg.modes || []; - for (const line of descriptionLines) { - if (line.includes('Issue 4 Only')) reg.issue_4_only = true; + for (const line of descriptionLines) { + reg.source.push(line); + const spaces_at_start = line.match(/^(\s*)/)?.[0].length || 0; + const trimmedLine = line.trim(); - const trimmedLine = line.trim(); - reg.source.push(line); - - if (trimmedLine.startsWith('//')) continue; - - if (trimmedLine.startsWith('(R)')) { - if (currentAccess) detail[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'read'; - continue; + if(spaces_at_start == 2) { + if (trimmedLine.startsWith('*')) { + console.log("PARENT",trimmedLine); + const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); + if (noteMatch) { + reg.notes.push({ + ref: noteMatch[1], + text: noteMatch[2], + }); } - if (trimmedLine.startsWith('(W)')) { - if (currentAccess) detail[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'write'; - continue; - } - if (trimmedLine.startsWith('(R/W')) { - if (currentAccess) detail[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'common'; - continue; - } - if (line.startsWith(trimmedLine)) { - if (currentAccess) detail[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = null; - } - + continue; + } else { if (currentAccess) { - const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); - const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); - - if (bitMatch) { - let bitDescription = bitMatch[3]; - const footnoteMatch = bitDescription.match(/(\*+)$/); - let footnoteRef: string | undefined = undefined; - if (footnoteMatch) { - footnoteRef = footnoteMatch[1]; - bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim(); - } - accessData.operations.push({ - bits: bitMatch[2], - description: bitDescription, - footnoteRef: footnoteRef, - }); - } else if (valueMatch) { - accessData.operations.push({ - bits: valueMatch[1].trim().replace(/\s/g, ''), - description: valueMatch[2].trim(), - }); - } else if (trimmedLine.startsWith('*')) { - // SPECIAL: treat star lines as headings in description rather than notes - const heading = trimmedLine.replace(/^\*+\s*/, '').trim(); - if (!accessData.description) accessData.description = ''; - accessData.description += (accessData.description ? '\n' : '') + heading; - } else if (trimmedLine) { - if (line.match(/^\s+/) && accessData.operations.length > 0) { - accessData.operations[accessData.operations.length - 1].description += `\n${line}`; - } else { - if (!accessData.description) { - accessData.description = ''; - } - accessData.description += `\n${trimmedLine}`; - } - } - } else { - if (trimmedLine.startsWith('*')) { - // Outside access blocks, keep notes as-is but attach to detail now - const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); - if (noteMatch) { - detail.notes.push({ - ref: noteMatch[1], - text: noteMatch[2], - }); - } - } else if (trimmedLine) { - detail.text += `${line}\n`; - } + // finalize previous access block into detail + detail[currentAccess] = accessData; } + reg.modes.push(detail); + detail = {read: undefined, write: undefined, common: undefined, text: ''}; + detail.modeName = trimmedLine; + + accessData = {operations: [], notes: []}; + currentAccess = null; + continue; + } } - if (currentAccess) { + + if (line.includes('Issue 4 Only')) reg.issue_4_only = true; + + + if (trimmedLine.startsWith('//')) continue; + + if (trimmedLine.startsWith('(R)')) { + if (currentAccess) { + // finalize previous access block into detail detail[currentAccess] = accessData; + } + accessData = { operations: [], notes: [] }; + currentAccess = 'read'; + continue; } - reg.modes = reg.modes || []; - reg.modes.push(detail); + if (trimmedLine.startsWith('(W)')) { + if (currentAccess) { + detail[currentAccess] = accessData; + } + accessData = { operations: [], notes: [] }; + currentAccess = 'write'; + continue; + } + if (trimmedLine.startsWith('(R/W')) { + if (currentAccess) { + detail[currentAccess] = accessData; + } + accessData = { operations: [], notes: [] }; + currentAccess = 'common'; + continue; + } + + if (currentAccess) { + const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); + const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); + + if (bitMatch) { + let bitDescription = bitMatch[3]; + const footnoteMatch = bitDescription.match(/(\*+)$/); + let footnoteRef: string | undefined = undefined; + if (footnoteMatch) { + console.log("FOOTNOTE",footnoteMatch); + footnoteRef = footnoteMatch[1]; + bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim(); + } + accessData.operations.push({ + bits: bitMatch[2], + description: bitDescription, + footnoteRef: footnoteRef, + }); + } else if (valueMatch) { + accessData.operations.push({ + bits: valueMatch[1].trim().replace(/\s/g, ''), + description: valueMatch[2].trim(), + }); + } else if (trimmedLine.startsWith('*') && spaces_at_start > 2) { + const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); + if (noteMatch) { + reg.notes.push({ + ref: noteMatch[1], + text: noteMatch[2], + }); + } + } + } else if (trimmedLine) { + console.log("LINE", trimmedLine); + if (line.match(/^\s+/) && accessData.operations.length > 0) { + accessData.operations[accessData.operations.length - 1].description += `\n${line}`; + } else { + if (!accessData.description) { + accessData.description = ''; + } + accessData.description += `\n${trimmedLine}`; + } + } + } + if (currentAccess) { + detail[currentAccess] = accessData; + console.log("FINAL",detail,currentAccess); + } + + // Push the parsed detail into modes + reg.modes.push(detail); };