Moving towards multiple register parsers, to handle more exotic register types
This commit is contained in:
@@ -74,12 +74,6 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
|||||||
register.description.toLowerCase().includes(searchTerm.toLowerCase())
|
register.description.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
);
|
);
|
||||||
|
|
||||||
const getDefaultActiveKey = (register: Register) => {
|
|
||||||
if (register.common) return 'common';
|
|
||||||
if (register.read) return 'read';
|
|
||||||
if (register.write) return 'write';
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container fluid>
|
<Container fluid>
|
||||||
@@ -92,12 +86,9 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Row>
|
<Row>
|
||||||
{filteredRegisters.map(register => {
|
{filteredRegisters.map(register => (
|
||||||
const defaultActiveKey = getDefaultActiveKey(register);
|
<RegisterDetail key={register.hex_address} register={register} />
|
||||||
return (
|
))}
|
||||||
<RegisterDetail key={register.hex_address} register={register} defaultActiveKey={defaultActiveKey} />
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,15 +10,12 @@ import * as Icon from 'react-bootstrap-icons';
|
|||||||
/**
|
/**
|
||||||
* A client-side component that displays the details of a single register.
|
* A client-side component that displays the details of a single register.
|
||||||
* @param register The register object to display.
|
* @param register The register object to display.
|
||||||
* @param defaultActiveKey The default active tab to display.
|
|
||||||
* @returns A React component that displays the register details.
|
* @returns A React component that displays the register details.
|
||||||
*/
|
*/
|
||||||
export default function RegisterDetail({
|
export default function RegisterDetail({
|
||||||
register,
|
register,
|
||||||
defaultActiveKey,
|
|
||||||
}: {
|
}: {
|
||||||
register: Register;
|
register: Register;
|
||||||
defaultActiveKey?: string;
|
|
||||||
}) {
|
}) {
|
||||||
const [showSource, setShowSource] = useState(false);
|
const [showSource, setShowSource] = useState(false);
|
||||||
|
|
||||||
@@ -27,9 +24,7 @@ export default function RegisterDetail({
|
|||||||
<Card>
|
<Card>
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
<code>{register.hex_address}</code> ( {register.dec_address} )
|
<code>{register.hex_address}</code> ( {register.dec_address} )
|
||||||
{/*<Link href={`https://wiki.specnext.dev/${encodeURIComponent((register.name).replace(' ','_'))}_Register`} className="text-decoration-none">*/}
|
|
||||||
<strong>{register.name}</strong> {register.issue_4_only && <span className="badge bg-danger">Issue 4 Only</span>}
|
<strong>{register.name}</strong> {register.issue_4_only && <span className="badge bg-danger">Issue 4 Only</span>}
|
||||||
{/*</Link>*/}
|
|
||||||
<div className="float-end small text-muted">
|
<div className="float-end small text-muted">
|
||||||
<Link href={`https://wiki.specnext.dev/${encodeURIComponent((register.name).replace(' ','_'))}_Register`} className="text-decoration-none btn btn-sm btn-primary" title="Open wiki">
|
<Link href={`https://wiki.specnext.dev/${encodeURIComponent((register.name).replace(' ','_'))}_Register`} className="text-decoration-none btn btn-sm btn-primary" title="Open wiki">
|
||||||
<Icon.Wikipedia />
|
<Icon.Wikipedia />
|
||||||
@@ -45,20 +40,27 @@ export default function RegisterDetail({
|
|||||||
</div>
|
</div>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Tabs defaultActiveKey={defaultActiveKey} id={`register-tabs-${register.hex_address}`}>
|
{ register.modes.map((mode, idx) => (
|
||||||
{register.common && <Tab eventKey="common" title="Read/Write">{renderAccess(register.common)}</Tab>}
|
<div key={idx} className={idx > 0 ? 'mt-4' : ''}>
|
||||||
{register.read && <Tab eventKey="read" title="Read">{renderAccess(register.read)}</Tab>}
|
{register.modes.length > 1 && (
|
||||||
{register.write && <Tab eventKey="write" title="Write">{renderAccess(register.write)}</Tab>}
|
<h5 className="mb-3">Mode {idx + 1}</h5>
|
||||||
|
)}
|
||||||
|
<Tabs id={`register-tabs-${register.hex_address}-${idx}`}>
|
||||||
|
{mode.common && <Tab eventKey="common" title="Read/Write">{renderAccess(mode.common)}</Tab>}
|
||||||
|
{mode.read && <Tab eventKey="read" title="Read">{renderAccess(mode.read)}</Tab>}
|
||||||
|
{mode.write && <Tab eventKey="write" title="Write">{renderAccess(mode.write)}</Tab>}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{register.notes.map((note, index) => (
|
{mode.notes && mode.notes.map((note, index) => (
|
||||||
<p key={index} className="small text-muted">{note.ref} {note.text}</p>
|
<p key={index} className="small text-muted">{note.ref} {note.text}</p>
|
||||||
))}
|
))}
|
||||||
{register.text && register.text.length > 0 && (
|
{mode.text && mode.text.length > 0 && (
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<h5>Notes:</h5>
|
<h6>Notes:</h6>
|
||||||
<pre>{register.text}</pre>
|
<pre>{mode.text}</pre>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export default async function RegisterDetailPage({ params }: { params: { hex: st
|
|||||||
|
|
||||||
if (!register) return notFound();
|
if (!register) return notFound();
|
||||||
|
|
||||||
const defaultActiveKey = register.common ? 'common' : (register.read ? 'read' : (register.write ? 'write' : undefined));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container fluid className="py-4">
|
<Container fluid className="py-4">
|
||||||
@@ -21,7 +20,7 @@ export default async function RegisterDetailPage({ params }: { params: { hex: st
|
|||||||
<Link href="/registers" className="btn btn-secondary">← Back to Registers</Link>
|
<Link href="/registers" className="btn btn-secondary">← Back to Registers</Link>
|
||||||
</div>
|
</div>
|
||||||
<Row>
|
<Row>
|
||||||
<RegisterDetail register={register} defaultActiveKey={defaultActiveKey} />
|
<RegisterDetail register={register} />
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,16 +18,21 @@ export interface RegisterAccess {
|
|||||||
operations: RegisterBitwiseOperation[];
|
operations: RegisterBitwiseOperation[];
|
||||||
notes: Note[];
|
notes: Note[];
|
||||||
}
|
}
|
||||||
export interface Register {
|
|
||||||
hex_address: string;
|
export interface RegisterDetail {
|
||||||
dec_address: number | string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
read?: RegisterAccess;
|
read?: RegisterAccess;
|
||||||
write?: RegisterAccess;
|
write?: RegisterAccess;
|
||||||
common?: RegisterAccess;
|
common?: RegisterAccess;
|
||||||
text: string;
|
text: string;
|
||||||
notes: Note[];
|
notes: Note[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Register {
|
||||||
|
hex_address: string;
|
||||||
|
dec_address: number | string;
|
||||||
|
name: string;
|
||||||
|
modes: RegisterDetail[];
|
||||||
|
description: string;
|
||||||
issue_4_only: boolean;
|
issue_4_only: boolean;
|
||||||
source: string[];
|
source: string[];
|
||||||
}
|
}
|
||||||
@@ -84,8 +89,7 @@ export function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
dec_address: dec,
|
dec_address: dec,
|
||||||
name: regName,
|
name: regName,
|
||||||
description: description,
|
description: description,
|
||||||
notes: [],
|
modes: [],
|
||||||
text: "",
|
|
||||||
issue_4_only: false,
|
issue_4_only: false,
|
||||||
source: []
|
source: []
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import {Register, RegisterAccess} from "@/utils/register_parser";
|
import {Register, RegisterAccess, RegisterDetail} from "@/utils/register_parser";
|
||||||
|
|
||||||
export const parseDescriptionDefault = (reg: Register, description: string) => {
|
export const parseDescriptionDefault = (reg: Register, description: string) => {
|
||||||
const descriptionLines = description.split('\n');
|
const descriptionLines = description.split('\n');
|
||||||
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
||||||
let accessData: RegisterAccess = { operations: [], notes: [] };
|
let accessData: RegisterAccess = { operations: [], notes: [] };
|
||||||
|
// Prepare a new RegisterDetail for this description block
|
||||||
|
const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: '', notes: [] };
|
||||||
|
|
||||||
for (const line of descriptionLines) {
|
for (const line of descriptionLines) {
|
||||||
if (line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
if (line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
||||||
@@ -14,25 +16,34 @@ export const parseDescriptionDefault = (reg: Register, description: string) => {
|
|||||||
if (trimmedLine.startsWith('//')) continue;
|
if (trimmedLine.startsWith('//')) continue;
|
||||||
|
|
||||||
if (trimmedLine.startsWith('(R)')) {
|
if (trimmedLine.startsWith('(R)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) {
|
||||||
|
// finalize previous access block into detail
|
||||||
|
detail[currentAccess] = accessData;
|
||||||
|
}
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'read';
|
currentAccess = 'read';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine.startsWith('(W)')) {
|
if (trimmedLine.startsWith('(W)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) {
|
||||||
|
detail[currentAccess] = accessData;
|
||||||
|
}
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'write';
|
currentAccess = 'write';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine.startsWith('(R/W')) {
|
if (trimmedLine.startsWith('(R/W')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) {
|
||||||
|
detail[currentAccess] = accessData;
|
||||||
|
}
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'common';
|
currentAccess = 'common';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(trimmedLine)) {
|
if (line.startsWith(trimmedLine)) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) {
|
||||||
|
detail[currentAccess] = accessData;
|
||||||
|
}
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = null;
|
currentAccess = null;
|
||||||
}
|
}
|
||||||
@@ -81,17 +92,20 @@ export const parseDescriptionDefault = (reg: Register, description: string) => {
|
|||||||
if (trimmedLine.startsWith('*')) {
|
if (trimmedLine.startsWith('*')) {
|
||||||
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
if (noteMatch) {
|
if (noteMatch) {
|
||||||
reg.notes.push({
|
detail.notes.push({
|
||||||
ref: noteMatch[1],
|
ref: noteMatch[1],
|
||||||
text: noteMatch[2],
|
text: noteMatch[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (trimmedLine) {
|
} else if (trimmedLine) {
|
||||||
reg.text += `${line}\n`;
|
detail.text += `${line}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentAccess) {
|
if (currentAccess) {
|
||||||
reg[currentAccess] = accessData;
|
detail[currentAccess] = accessData;
|
||||||
}
|
}
|
||||||
|
// Push the parsed detail into modes
|
||||||
|
reg.modes = reg.modes || [];
|
||||||
|
reg.modes.push(detail);
|
||||||
};
|
};
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
|
|
||||||
// Special-case parser for 0xF0 (XDEV CMD): treat headings beginning with '*' inside access blocks
|
// 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.
|
// as descriptive text instead of notes, so sub-modes become part of the section descriptions.
|
||||||
import {Register, RegisterAccess} from "@/utils/register_parser";
|
import {Register, RegisterAccess, RegisterDetail} from "@/utils/register_parser";
|
||||||
|
|
||||||
export const parseDescriptionF0 = (reg: Register, description: string) => {
|
export const parseDescriptionF0 = (reg: Register, description: string) => {
|
||||||
const descriptionLines = description.split('\n');
|
const descriptionLines = description.split('\n');
|
||||||
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
||||||
let accessData: RegisterAccess = { operations: [], notes: [] };
|
let accessData: RegisterAccess = { operations: [], notes: [] };
|
||||||
|
const detail: RegisterDetail = { read: undefined, write: undefined, common: undefined, text: '', notes: [] };
|
||||||
|
|
||||||
for (const line of descriptionLines) {
|
for (const line of descriptionLines) {
|
||||||
if (line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
if (line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
||||||
@@ -17,25 +18,25 @@ export const parseDescriptionF0 = (reg: Register, description: string) => {
|
|||||||
if (trimmedLine.startsWith('//')) continue;
|
if (trimmedLine.startsWith('//')) continue;
|
||||||
|
|
||||||
if (trimmedLine.startsWith('(R)')) {
|
if (trimmedLine.startsWith('(R)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) detail[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'read';
|
currentAccess = 'read';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine.startsWith('(W)')) {
|
if (trimmedLine.startsWith('(W)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) detail[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'write';
|
currentAccess = 'write';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine.startsWith('(R/W')) {
|
if (trimmedLine.startsWith('(R/W')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) detail[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'common';
|
currentAccess = 'common';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.startsWith(trimmedLine)) {
|
if (line.startsWith(trimmedLine)) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) detail[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = null;
|
currentAccess = null;
|
||||||
}
|
}
|
||||||
@@ -79,20 +80,22 @@ export const parseDescriptionF0 = (reg: Register, description: string) => {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (trimmedLine.startsWith('*')) {
|
if (trimmedLine.startsWith('*')) {
|
||||||
// Outside access blocks, keep notes as-is
|
// Outside access blocks, keep notes as-is but attach to detail now
|
||||||
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
if (noteMatch) {
|
if (noteMatch) {
|
||||||
reg.notes.push({
|
detail.notes.push({
|
||||||
ref: noteMatch[1],
|
ref: noteMatch[1],
|
||||||
text: noteMatch[2],
|
text: noteMatch[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (trimmedLine) {
|
} else if (trimmedLine) {
|
||||||
reg.text += `${line}\n`;
|
detail.text += `${line}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentAccess) {
|
if (currentAccess) {
|
||||||
reg[currentAccess] = accessData;
|
detail[currentAccess] = accessData;
|
||||||
}
|
}
|
||||||
|
reg.modes = reg.modes || [];
|
||||||
|
reg.modes.push(detail);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user