Formatting tables
This commit is contained in:
@@ -40,3 +40,10 @@ a {
|
|||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footnote-ref {
|
||||||
|
cursor: pointer;
|
||||||
|
color: blue;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import { Geist, Geist_Mono } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
|
|
||||||
const geistSans = Geist({
|
const geistSans = Geist({
|
||||||
variable: "--font-geist-sans",
|
variable: "--font-geist-sans",
|
||||||
|
|||||||
@@ -1,13 +1,57 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Register } from './types';
|
import { Register, RegisterAccess, Note } from './types';
|
||||||
import { Form, Card, Container, Row, Col } from 'react-bootstrap';
|
import { Form, Card, Container, Row, Col, Tabs, Tab, Table, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||||
|
|
||||||
interface RegisterBrowserProps {
|
interface RegisterBrowserProps {
|
||||||
registers: Register[];
|
registers: Register[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderAccess(access: RegisterAccess) {
|
||||||
|
const renderTooltip = (notes: Note[]) => (
|
||||||
|
<Tooltip id="tooltip">
|
||||||
|
{notes.map((note, index) => (
|
||||||
|
<div key={index}>{note.text}</div>
|
||||||
|
))}
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Table striped bordered hover size="sm" className="bits-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Bits</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{access.operations.map((op, index) => {
|
||||||
|
const notes = access.notes.filter(note => note.ref === op.footnoteRef);
|
||||||
|
return (
|
||||||
|
<tr key={index}>
|
||||||
|
<td>{op.bits}</td>
|
||||||
|
<td>
|
||||||
|
{op.description}
|
||||||
|
{op.footnoteRef && notes.length > 0 && (
|
||||||
|
<OverlayTrigger placement="top" overlay={renderTooltip(notes)}>
|
||||||
|
<span className="footnote-ref">{op.footnoteRef}</span>
|
||||||
|
</OverlayTrigger>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
{access.notes.map((note, index) => (
|
||||||
|
<p key={index} className="small text-muted">{note.ref} {note.text}</p>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
|
||||||
@@ -16,6 +60,13 @@ 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>
|
||||||
<Form.Group className="mb-3">
|
<Form.Group className="mb-3">
|
||||||
@@ -27,18 +78,28 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
|||||||
/>
|
/>
|
||||||
</Form.Group>
|
</Form.Group>
|
||||||
<Row>
|
<Row>
|
||||||
{filteredRegisters.map(register => (
|
{filteredRegisters.map(register => {
|
||||||
<Col key={register.hex_address} xs={12} md={6} lg={4} className="mb-4">
|
const defaultActiveKey = getDefaultActiveKey(register);
|
||||||
<Card>
|
return (
|
||||||
<Card.Header>
|
<Col key={register.hex_address} xs={12} className="mb-4">
|
||||||
<strong>{register.name}</strong> ({register.hex_address} / {register.dec_address})
|
<Card>
|
||||||
</Card.Header>
|
<Card.Header>
|
||||||
<Card.Body>
|
<strong>{register.name}</strong> ({register.hex_address} / {register.dec_address})
|
||||||
<pre style={{ whiteSpace: 'pre-wrap' }}>{register.description}</pre>
|
</Card.Header>
|
||||||
</Card.Body>
|
<Card.Body>
|
||||||
</Card>
|
<Tabs defaultActiveKey={defaultActiveKey} id={`register-tabs-${register.hex_address}`}>
|
||||||
</Col>
|
{register.common && <Tab eventKey="common" title="R/W">{renderAccess(register.common)}</Tab>}
|
||||||
))}
|
{register.read && <Tab eventKey="read" title="Read">{renderAccess(register.read)}</Tab>}
|
||||||
|
{register.write && <Tab eventKey="write" title="Write">{renderAccess(register.write)}</Tab>}
|
||||||
|
</Tabs>
|
||||||
|
{register.notes.map((note, index) => (
|
||||||
|
<p key={index} className="small text-muted">{note.ref} {note.text}</p>
|
||||||
|
))}
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</Row>
|
</Row>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import RegisterBrowser from './RegisterBrowser';
|
import RegisterBrowser from './RegisterBrowser';
|
||||||
import { Register } from './types';
|
import { Register, RegisterAccess } from './types';
|
||||||
|
|
||||||
async function parseNextReg(fileContent: string): Promise<Register[]> {
|
async function parseNextReg(fileContent: string): Promise<Register[]> {
|
||||||
const registers: Register[] = [];
|
const registers: Register[] = [];
|
||||||
@@ -11,77 +11,137 @@ async function parseNextReg(fileContent: string): Promise<Register[]> {
|
|||||||
if (!paragraph.trim()) {
|
if (!paragraph.trim()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
processRegisterBlock(paragraph, registers);
|
||||||
const lines = paragraph.trim().split('\n');
|
|
||||||
processRegisterBlock(lines, registers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return registers;
|
return registers;
|
||||||
}
|
}
|
||||||
|
|
||||||
function processRegisterBlock(lines: string[], registers: Register[]) {
|
function processRegisterBlock(paragraph: string, registers: Register[]) {
|
||||||
|
const lines = paragraph.trim().split('\n');
|
||||||
const firstLine = lines[0];
|
const firstLine = lines[0];
|
||||||
const restOfLines = lines.slice(1);
|
|
||||||
|
|
||||||
const multiRegisterMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\(.*?\)\s*=>\s*(.*)/);
|
const registerMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\((.*?)\)\s*=>\s*(.*)/);
|
||||||
|
|
||||||
if (multiRegisterMatch) {
|
if (!registerMatch) {
|
||||||
const hexAddresses = multiRegisterMatch[1].trim();
|
|
||||||
const decAddresses = multiRegisterMatch[2].trim();
|
|
||||||
const name = multiRegisterMatch[3] ? multiRegisterMatch[3].trim() : '';
|
|
||||||
const description = restOfLines.join('\n').trim();
|
|
||||||
|
|
||||||
const hexList = hexAddresses.split(',').map(h => h.trim());
|
|
||||||
const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim());
|
|
||||||
|
|
||||||
if (hexList.length > 1) {
|
|
||||||
for (let i = 0; i < hexList.length; i++) {
|
|
||||||
const hexAddr = hexList[i];
|
|
||||||
const decAddr = decList[i] || decAddresses;
|
|
||||||
registers.push({
|
|
||||||
hex_address: hexAddr,
|
|
||||||
dec_address: isNaN(parseInt(decAddr, 10)) ? decAddr : parseInt(decAddr, 10),
|
|
||||||
name: `${name} (${hexAddr})`,
|
|
||||||
description: description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
registers.push({
|
|
||||||
hex_address: hexAddresses,
|
|
||||||
dec_address: isNaN(parseInt(decAddresses, 10)) ? decAddresses : parseInt(decAddresses, 10),
|
|
||||||
name: name,
|
|
||||||
description: description,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const singleRegisterMatch = lines[0].match(/(0x[0-9a-fA-F]{2})\s+(\d+)\s+=>\s+(.*)/);
|
const hexAddresses = registerMatch[1].trim();
|
||||||
if(singleRegisterMatch) {
|
const decAddresses = registerMatch[2].trim();
|
||||||
const description = lines.slice(1).join('\n').trim();
|
const name = registerMatch[3] ? registerMatch[3].trim() : '';
|
||||||
const hexAddr = singleRegisterMatch[1].trim();
|
const description = lines.slice(1).join('\n').trim();
|
||||||
const decAddr = singleRegisterMatch[2].trim();
|
|
||||||
const name = singleRegisterMatch[3].trim();
|
const hexList = hexAddresses.split(',').map(h => h.trim());
|
||||||
|
const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim());
|
||||||
registers.push({
|
|
||||||
hex_address: hexAddr,
|
const createRegister = (hex: string, dec: string | number, regName: string): Register => {
|
||||||
dec_address: parseInt(decAddr, 10),
|
const reg: Register = {
|
||||||
name: name,
|
hex_address: hex,
|
||||||
|
dec_address: dec,
|
||||||
|
name: regName,
|
||||||
description: description,
|
description: description,
|
||||||
});
|
notes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const descriptionLines = description.split('\n');
|
||||||
|
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
||||||
|
let accessData: RegisterAccess = { operations: [], notes: [] };
|
||||||
|
|
||||||
|
for (const line of descriptionLines) {
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
if (trimmedLine === '(R)') {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'read';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmedLine === '(W)') {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'write';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmedLine === '(R/W)') {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'common';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentAccess) {
|
||||||
|
const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\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 (trimmedLine.startsWith('*')) {
|
||||||
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
|
if (noteMatch) {
|
||||||
|
accessData.notes.push({
|
||||||
|
ref: noteMatch[1],
|
||||||
|
text: noteMatch[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if(trimmedLine) {
|
||||||
|
if(accessData.operations.length > 0) {
|
||||||
|
accessData.operations[accessData.operations.length-1].description += `\n${trimmedLine}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (trimmedLine.startsWith('*')) {
|
||||||
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
|
if (noteMatch) {
|
||||||
|
reg.notes.push({
|
||||||
|
ref: noteMatch[1],
|
||||||
|
text: noteMatch[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentAccess) {
|
||||||
|
reg[currentAccess] = accessData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hexList.length > 1) {
|
||||||
|
for (let i = 0; i < hexList.length; i++) {
|
||||||
|
const hexAddr = hexList[i];
|
||||||
|
const decAddr = decList[i] || decAddresses;
|
||||||
|
const dec = isNaN(parseInt(decAddr, 10)) ? decAddr : parseInt(decAddr, 10);
|
||||||
|
registers.push(createRegister(hexAddr, dec, `${name} (${hexAddr})`));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const dec = isNaN(parseInt(decAddresses, 10)) ? decAddresses : parseInt(decAddresses, 10);
|
||||||
|
registers.push(createRegister(hexAddresses, dec, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
import { Container } from 'react-bootstrap';
|
||||||
|
|
||||||
|
|
||||||
export default async function RegistersPage() {
|
export default async function RegistersPage() {
|
||||||
const filePath = path.join(process.cwd(), 'data', 'nextreg_bare.txt');
|
const filePath = path.join(process.cwd(), 'data', 'nextreg_bare.txt');
|
||||||
const fileContent = await fs.readFile(filePath, 'utf8');
|
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||||
const registers = await parseNextReg(fileContent);
|
const registers = await parseNextReg(fileContent);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<Container fluid className="py-4">
|
||||||
<h1>Spectrum Next Registers</h1>
|
<h1 className="mb-4">Spectrum Next Registers</h1>
|
||||||
<RegisterBrowser registers={registers} />
|
<RegisterBrowser registers={registers} />
|
||||||
</div>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
export interface BitwiseOperation {
|
export interface BitwiseOperation {
|
||||||
bits: string;
|
bits: string;
|
||||||
description: string;
|
description: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
notes?: string[];
|
footnoteRef?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RegisterAccess {
|
export interface Note {
|
||||||
description?: string;
|
ref: string;
|
||||||
operations: BitwiseOperation[];
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Register {
|
export interface RegisterAccess {
|
||||||
hex_address: string;
|
operations: BitwiseOperation[];
|
||||||
dec_address: number | string;
|
notes: Note[];
|
||||||
name: string;
|
}
|
||||||
description: string;
|
|
||||||
read?: RegisterAccess;
|
export interface Register {
|
||||||
write?: RegisterAccess;
|
hex_address: string;
|
||||||
notes?: string[];
|
dec_address: number | string;
|
||||||
}
|
name: string;
|
||||||
|
description: string;
|
||||||
|
read?: RegisterAccess;
|
||||||
|
write?: RegisterAccess;
|
||||||
|
common?: RegisterAccess;
|
||||||
|
notes: Note[];
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user