diff --git a/src/app/globals.css b/src/app/globals.css
index e3734be..a6f1d74 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -40,3 +40,10 @@ a {
color-scheme: dark;
}
}
+
+.footnote-ref {
+ cursor: pointer;
+ color: blue;
+ margin-left: 4px;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 42fc323..29dee48 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,6 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
+import "bootstrap/dist/css/bootstrap.min.css";
const geistSans = Geist({
variable: "--font-geist-sans",
diff --git a/src/app/registers/RegisterBrowser.tsx b/src/app/registers/RegisterBrowser.tsx
index d6b6e97..7ec30b8 100644
--- a/src/app/registers/RegisterBrowser.tsx
+++ b/src/app/registers/RegisterBrowser.tsx
@@ -1,13 +1,57 @@
'use client';
import { useState } from 'react';
-import { Register } from './types';
-import { Form, Card, Container, Row, Col } from 'react-bootstrap';
+import { Register, RegisterAccess, Note } from './types';
+import { Form, Card, Container, Row, Col, Tabs, Tab, Table, OverlayTrigger, Tooltip } from 'react-bootstrap';
interface RegisterBrowserProps {
registers: Register[];
}
+function renderAccess(access: RegisterAccess) {
+ const renderTooltip = (notes: Note[]) => (
+
+ {notes.map((note, index) => (
+ {note.text}
+ ))}
+
+ );
+
+ return (
+ <>
+
+
+
+ | Bits |
+ Description |
+
+
+
+ {access.operations.map((op, index) => {
+ const notes = access.notes.filter(note => note.ref === op.footnoteRef);
+ return (
+
+ | {op.bits} |
+
+ {op.description}
+ {op.footnoteRef && notes.length > 0 && (
+
+ {op.footnoteRef}
+
+ )}
+ |
+
+ );
+ })}
+
+
+ {access.notes.map((note, index) => (
+ {note.ref} {note.text}
+ ))}
+ >
+ );
+}
+
export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
const [searchTerm, setSearchTerm] = useState('');
@@ -16,6 +60,13 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
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 (
@@ -27,18 +78,28 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
/>
- {filteredRegisters.map(register => (
-
-
-
- {register.name} ({register.hex_address} / {register.dec_address})
-
-
- {register.description}
-
-
-
- ))}
+ {filteredRegisters.map(register => {
+ const defaultActiveKey = getDefaultActiveKey(register);
+ return (
+
+
+
+ {register.name} ({register.hex_address} / {register.dec_address})
+
+
+
+ {register.common && {renderAccess(register.common)}}
+ {register.read && {renderAccess(register.read)}}
+ {register.write && {renderAccess(register.write)}}
+
+ {register.notes.map((note, index) => (
+ {note.ref} {note.text}
+ ))}
+
+
+
+ );
+ })}
);
diff --git a/src/app/registers/page.tsx b/src/app/registers/page.tsx
index 8d35f32..07f87ce 100644
--- a/src/app/registers/page.tsx
+++ b/src/app/registers/page.tsx
@@ -1,7 +1,7 @@
import { promises as fs } from 'fs';
import path from 'path';
import RegisterBrowser from './RegisterBrowser';
-import { Register } from './types';
+import { Register, RegisterAccess } from './types';
async function parseNextReg(fileContent: string): Promise {
const registers: Register[] = [];
@@ -11,77 +11,137 @@ async function parseNextReg(fileContent: string): Promise {
if (!paragraph.trim()) {
continue;
}
-
- const lines = paragraph.trim().split('\n');
- processRegisterBlock(lines, registers);
+ processRegisterBlock(paragraph, 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 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) {
- 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,
- });
- }
+ if (!registerMatch) {
return;
}
-
- const singleRegisterMatch = lines[0].match(/(0x[0-9a-fA-F]{2})\s+(\d+)\s+=>\s+(.*)/);
- if(singleRegisterMatch) {
- const description = lines.slice(1).join('\n').trim();
- const hexAddr = singleRegisterMatch[1].trim();
- const decAddr = singleRegisterMatch[2].trim();
- const name = singleRegisterMatch[3].trim();
-
- registers.push({
- hex_address: hexAddr,
- dec_address: parseInt(decAddr, 10),
- name: name,
+
+ const hexAddresses = registerMatch[1].trim();
+ const decAddresses = registerMatch[2].trim();
+ const name = registerMatch[3] ? registerMatch[3].trim() : '';
+ const description = lines.slice(1).join('\n').trim();
+
+ const hexList = hexAddresses.split(',').map(h => h.trim());
+ const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim());
+
+ const createRegister = (hex: string, dec: string | number, regName: string): Register => {
+ const reg: Register = {
+ hex_address: hex,
+ dec_address: dec,
+ name: regName,
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() {
const filePath = path.join(process.cwd(), 'data', 'nextreg_bare.txt');
const fileContent = await fs.readFile(filePath, 'utf8');
const registers = await parseNextReg(fileContent);
return (
-
-
Spectrum Next Registers
+
+ Spectrum Next Registers
-
+
);
}
diff --git a/src/app/registers/types.ts b/src/app/registers/types.ts
index 4e2f6dd..89eb6fb 100644
--- a/src/app/registers/types.ts
+++ b/src/app/registers/types.ts
@@ -1,21 +1,27 @@
-export interface BitwiseOperation {
- bits: string;
- description: string;
- value?: string;
- notes?: string[];
-}
+ export interface BitwiseOperation {
+ bits: string;
+ description: string;
+ value?: string;
+ footnoteRef?: string;
+ }
-export interface RegisterAccess {
- description?: string;
- operations: BitwiseOperation[];
-}
+ export interface Note {
+ ref: string;
+ text: string;
+ }
-export interface Register {
- hex_address: string;
- dec_address: number | string;
- name: string;
- description: string;
- read?: RegisterAccess;
- write?: RegisterAccess;
- notes?: string[];
-}
+ export interface RegisterAccess {
+ operations: BitwiseOperation[];
+ notes: Note[];
+ }
+
+ export interface Register {
+ hex_address: string;
+ dec_address: number | string;
+ name: string;
+ description: string;
+ read?: RegisterAccess;
+ write?: RegisterAccess;
+ common?: RegisterAccess;
+ notes: Note[];
+ }