Trying to get a dokku build :)
This commit is contained in:
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
FROM node:22.14.0-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install pnpm
|
||||||
|
RUN npm install -g pnpm
|
||||||
|
|
||||||
|
# Copy package.json and pnpm-lock.yaml
|
||||||
|
COPY package.json pnpm-lock.yaml* ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN pnpm install
|
||||||
|
|
||||||
|
# Copy the rest of the application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Create a volume mount point for sensitive data
|
||||||
|
VOLUME /app/data
|
||||||
|
|
||||||
|
# Expose the port the app runs on
|
||||||
|
#EXPOSE 5000
|
||||||
|
|
||||||
|
# Create a startup script that initializes the database and starts the application
|
||||||
|
COPY docker-entrypoint.sh /app/
|
||||||
|
RUN chmod +x /app/docker-entrypoint.sh
|
||||||
|
|
||||||
|
# Command to run the startup script
|
||||||
|
CMD ["/app/docker-entrypoint.sh"]
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
0x00 (00) => Machine ID
|
0x00 (00) => Machine ID
|
||||||
(R)
|
(R)
|
||||||
0000 1000 = EMULATORS
|
0000 1000 = EMULATORS
|
||||||
|
//
|
||||||
0000 1010 = ZX Spectrum Next
|
0000 1010 = ZX Spectrum Next
|
||||||
1111 1010 = ZX Spectrum Next Anti-brick
|
1111 1010 = ZX Spectrum Next Anti-brick
|
||||||
|
//
|
||||||
1001 1010 = ZX Spectrum Next Core on UnAmiga Reloaded
|
1001 1010 = ZX Spectrum Next Core on UnAmiga Reloaded
|
||||||
1010 1010 = ZX Spectrum Next Core on UnAmiga
|
1010 1010 = ZX Spectrum Next Core on UnAmiga
|
||||||
1011 1010 = ZX Spectrum Next Core on SiDi
|
1011 1010 = ZX Spectrum Next Core on SiDi
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
** These signals are ignored if the multiface, divmmc, dma or external nmi master is active
|
** These signals are ignored if the multiface, divmmc, dma or external nmi master is active
|
||||||
** Copper cannot clear these bits
|
** Copper cannot clear these bits
|
||||||
** An i/o trap could occur at the same time as mf / divmmc cause; always check this bit in nmi isr if important
|
** An i/o trap could occur at the same time as mf / divmmc cause; always check this bit in nmi isr if important
|
||||||
|
|
||||||
0x03 (03) => Machine Type
|
0x03 (03) => Machine Type
|
||||||
(R)
|
(R)
|
||||||
bit 7 = nextreg 0x44 second byte indicator
|
bit 7 = nextreg 0x44 second byte indicator
|
||||||
@@ -1234,6 +1234,4 @@ progress is made in the main program.
|
|||||||
bits 7:0 = MSB data connected to XADC DRP data bus D15:8
|
bits 7:0 = MSB data connected to XADC DRP data bus D15:8
|
||||||
* DRP reads store result here, DRP writes take value from here
|
* DRP reads store result here, DRP writes take value from here
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
0xFF (255) => Reserved for internal use
|
0xFF (255) => Reserved for internal use
|
||||||
6
docker-entrypoint.sh
Normal file
6
docker-entrypoint.sh
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Start the application
|
||||||
|
echo "Starting the application..."
|
||||||
|
exec pnpm start
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
@@ -46,4 +46,13 @@ a {
|
|||||||
color: blue;
|
color: blue;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bits-table th:first-child,
|
||||||
|
.bits-table td:first-child {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bits-table td:last-child {
|
||||||
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
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 Link from 'next/link';
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import "bootstrap/dist/css/bootstrap.min.css";
|
import "bootstrap/dist/css/bootstrap.min.css";
|
||||||
|
|
||||||
@@ -14,8 +15,8 @@ const geistMono = Geist_Mono({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Create Next App",
|
title: "Spectrum Next Registers",
|
||||||
description: "Generated by create next app",
|
description: "A platform for exploring the Spectrum Next registers",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -26,7 +27,27 @@ export default function RootLayout({
|
|||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
<body className={`${geistSans.variable} ${geistMono.variable}`}>
|
||||||
{children}
|
<nav className="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
|
||||||
|
<div className="container-fluid">
|
||||||
|
<Link className="navbar-brand" href="/">Next Explorer</Link>
|
||||||
|
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span className="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div className="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link className="nav-link" href="/">Home</Link>
|
||||||
|
</li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<Link className="nav-link" href="/registers">Registers</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div className="container-fluid py-3">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,98 +1,15 @@
|
|||||||
import Image from "next/image";
|
|
||||||
import styles from "./page.module.css";
|
import styles from "./page.module.css";
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<main className={styles.main}>
|
<main className={styles.main}>
|
||||||
<Image
|
|
||||||
className={styles.logo}
|
|
||||||
src="/next.svg"
|
|
||||||
alt="Next.js logo"
|
|
||||||
width={180}
|
|
||||||
height={38}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<ol>
|
|
||||||
<li>
|
|
||||||
Get started by editing <code>src/app/page.tsx</code>.
|
|
||||||
</li>
|
|
||||||
<li>Save and see your changes instantly.</li>
|
|
||||||
<li>
|
|
||||||
Explore the <a href="/registers">Spectrum Next Registers</a>.
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div className={styles.ctas}>
|
<Link href="/registers">
|
||||||
<a
|
Register Explorer →
|
||||||
className={styles.primary}
|
</Link>
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className={styles.logo}
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
Deploy now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className={styles.secondary}
|
|
||||||
>
|
|
||||||
Read our docs
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
<footer className={styles.footer}>
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/file.svg"
|
|
||||||
alt="File icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Learn
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/window.svg"
|
|
||||||
alt="Window icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Examples
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/globe.svg"
|
|
||||||
alt="Globe icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Go to nextjs.org →
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
import { Register, RegisterAccess, Note } from './types';
|
import { Register, RegisterAccess, Note } from './types';
|
||||||
import { Form, Card, Container, Row, Col, Tabs, Tab, Table, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
import { Form, Card, Container, Row, Col, Tabs, Tab, Table, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ interface RegisterBrowserProps {
|
|||||||
registers: Register[];
|
registers: Register[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAccess(access: RegisterAccess) {
|
export function renderAccess(access: RegisterAccess) {
|
||||||
const renderTooltip = (notes: Note[]) => (
|
const renderTooltip = (notes: Note[]) => (
|
||||||
<Tooltip id="tooltip">
|
<Tooltip id="tooltip">
|
||||||
{notes.map((note, index) => (
|
{notes.map((note, index) => (
|
||||||
@@ -19,32 +20,35 @@ function renderAccess(access: RegisterAccess) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Table striped bordered hover size="sm" className="bits-table">
|
{access.description && <pre>{access.description}</pre>}
|
||||||
<thead>
|
{access.operations.length > 0 &&
|
||||||
<tr>
|
<Table striped bordered hover size="sm" className="bits-table">
|
||||||
<th>Bits</th>
|
<thead>
|
||||||
<th>Description</th>
|
<tr>
|
||||||
</tr>
|
<th>Bits</th>
|
||||||
</thead>
|
<th>Description</th>
|
||||||
<tbody>
|
</tr>
|
||||||
{access.operations.map((op, index) => {
|
</thead>
|
||||||
const notes = access.notes.filter(note => note.ref === op.footnoteRef);
|
<tbody>
|
||||||
return (
|
{access.operations.map((op, index) => {
|
||||||
<tr key={index}>
|
const notes = access.notes.filter(note => note.ref === op.footnoteRef);
|
||||||
<td>{op.bits}</td>
|
return (
|
||||||
<td>
|
<tr key={index}>
|
||||||
{op.description}
|
<td>{op.bits}</td>
|
||||||
{op.footnoteRef && notes.length > 0 && (
|
<td>
|
||||||
<OverlayTrigger placement="top" overlay={renderTooltip(notes)}>
|
{op.description}
|
||||||
<span className="footnote-ref">{op.footnoteRef}</span>
|
{op.footnoteRef && notes.length > 0 && (
|
||||||
</OverlayTrigger>
|
<OverlayTrigger placement="top" overlay={renderTooltip(notes)}>
|
||||||
)}
|
<span className="footnote-ref">{op.footnoteRef}</span>
|
||||||
</td>
|
</OverlayTrigger>
|
||||||
</tr>
|
)}
|
||||||
);
|
</td>
|
||||||
})}
|
</tr>
|
||||||
</tbody>
|
);
|
||||||
</Table>
|
})}
|
||||||
|
</tbody>
|
||||||
|
</Table>
|
||||||
|
}
|
||||||
{access.notes.map((note, index) => (
|
{access.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>
|
||||||
))}
|
))}
|
||||||
@@ -84,17 +88,25 @@ export default function RegisterBrowser({ registers }: RegisterBrowserProps) {
|
|||||||
<Col key={register.hex_address} xs={12} className="mb-4">
|
<Col key={register.hex_address} xs={12} className="mb-4">
|
||||||
<Card>
|
<Card>
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
<strong>{register.name}</strong> ({register.hex_address} / {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>}
|
||||||
|
</Link>
|
||||||
</Card.Header>
|
</Card.Header>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Tabs defaultActiveKey={defaultActiveKey} id={`register-tabs-${register.hex_address}`}>
|
<Tabs defaultActiveKey={defaultActiveKey} id={`register-tabs-${register.hex_address}`}>
|
||||||
{register.common && <Tab eventKey="common" title="R/W">{renderAccess(register.common)}</Tab>}
|
{register.common && <Tab eventKey="common" title="Read/Write">{renderAccess(register.common)}</Tab>}
|
||||||
{register.read && <Tab eventKey="read" title="Read">{renderAccess(register.read)}</Tab>}
|
{register.read && <Tab eventKey="read" title="Read">{renderAccess(register.read)}</Tab>}
|
||||||
{register.write && <Tab eventKey="write" title="Write">{renderAccess(register.write)}</Tab>}
|
{register.write && <Tab eventKey="write" title="Write">{renderAccess(register.write)}</Tab>}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
{register.notes.map((note, index) => (
|
{register.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 && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<h5>Notes:</h5>
|
||||||
|
<pre>{register.text}</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
60
src/app/registers/RegisterDetailClient.tsx
Normal file
60
src/app/registers/RegisterDetailClient.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Container, Row, Col, Card, Tabs, Tab } from 'react-bootstrap';
|
||||||
|
import { Register } from './types';
|
||||||
|
import { renderAccess } from './RegisterBrowser';
|
||||||
|
|
||||||
|
export default function RegisterDetailClient({
|
||||||
|
register,
|
||||||
|
defaultActiveKey,
|
||||||
|
}: {
|
||||||
|
register: Register;
|
||||||
|
defaultActiveKey?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Container fluid className="py-4">
|
||||||
|
<Row>
|
||||||
|
<Col xs={12}>
|
||||||
|
<Card>
|
||||||
|
<Card.Header>
|
||||||
|
<strong>{register.name}</strong> ({register.hex_address} / {register.dec_address}){' '}
|
||||||
|
{register.issue_4_only && <span className="badge bg-danger">Issue 4 Only</span>}
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Body>
|
||||||
|
{defaultActiveKey ? (
|
||||||
|
<Tabs defaultActiveKey={defaultActiveKey} id={`register-tabs-${register.hex_address}`}>
|
||||||
|
{register.common && (
|
||||||
|
<Tab eventKey="common" title="Read/Write">
|
||||||
|
{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>
|
||||||
|
) : null}
|
||||||
|
{register.notes.map((note, index) => (
|
||||||
|
<p key={index} className="small text-muted">
|
||||||
|
{note.ref} {note.text}
|
||||||
|
</p>
|
||||||
|
))}
|
||||||
|
{register.text && register.text.length > 0 && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<h5>Notes:</h5>
|
||||||
|
<pre>{register.text}</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card.Body>
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
183
src/app/registers/[hex]/page.tsx
Normal file
183
src/app/registers/[hex]/page.tsx
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { notFound } from 'next/navigation';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { Register, RegisterAccess } from '../../registers/types';
|
||||||
|
import RegisterDetailClient from '../../registers/RegisterDetailClient';
|
||||||
|
|
||||||
|
async function parseNextReg(fileContent: string): Promise<Register[]> {
|
||||||
|
const registers: Register[] = [];
|
||||||
|
const paragraphs = fileContent.split(/\n\s*\n/);
|
||||||
|
|
||||||
|
for (const paragraph of paragraphs) {
|
||||||
|
if (!paragraph.trim()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
processRegisterBlock(paragraph, registers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return registers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processRegisterBlock(paragraph: string, registers: Register[]) {
|
||||||
|
const lines = paragraph.trim().split('\n');
|
||||||
|
const firstLine = lines[0];
|
||||||
|
|
||||||
|
const registerMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\((.*?)\)\s*=>\s*(.*)/);
|
||||||
|
|
||||||
|
if (!registerMatch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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: [],
|
||||||
|
text: "",
|
||||||
|
issue_4_only: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const descriptionLines = description.split('\n');
|
||||||
|
let currentAccess: 'read' | 'write' | 'common' | null = null;
|
||||||
|
let accessData: RegisterAccess = { operations: [], notes: [] };
|
||||||
|
|
||||||
|
for (const line of descriptionLines) {
|
||||||
|
if(line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
||||||
|
|
||||||
|
const trimmedLine = line.trim();
|
||||||
|
|
||||||
|
if (trimmedLine.startsWith('//')) continue;
|
||||||
|
|
||||||
|
if (trimmedLine.startsWith('(R)')) {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'read';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmedLine.startsWith('(W)')) {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'write';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (trimmedLine.startsWith('(R/W')) {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = 'common';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.startsWith(trimmedLine)) {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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('*')) {
|
||||||
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
|
if (noteMatch) {
|
||||||
|
accessData.notes.push({
|
||||||
|
ref: noteMatch[1],
|
||||||
|
text: noteMatch[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} 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('*')) {
|
||||||
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
|
if (noteMatch) {
|
||||||
|
(reg as Register).notes.push({
|
||||||
|
ref: noteMatch[1],
|
||||||
|
text: noteMatch[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
reg.text += `${line}\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentAccess) {
|
||||||
|
(reg as Register)[currentAccess] = accessData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hexList.length > 1) {
|
||||||
|
for (let i = 0; i < hexList.length; i++) {
|
||||||
|
const hexAddr = hexList[i];
|
||||||
|
const decAddr = (decList as string[])[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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function RegisterDetailPage({ params }: { params: { hex: string } }) {
|
||||||
|
const filePath = path.join(process.cwd(), 'data', 'nextreg_records.txt');
|
||||||
|
const fileContent = await fs.readFile(filePath, 'utf8');
|
||||||
|
const registers = await parseNextReg(fileContent);
|
||||||
|
|
||||||
|
const targetHex = decodeURIComponent((await params).hex).toLowerCase();
|
||||||
|
|
||||||
|
const register = registers.find(r => r.hex_address.toLowerCase() === targetHex);
|
||||||
|
|
||||||
|
if (!register) return notFound();
|
||||||
|
|
||||||
|
const defaultActiveKey = register.common ? 'common' : (register.read ? 'read' : (register.write ? 'write' : undefined));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="py-4">
|
||||||
|
<div className="mb-3">
|
||||||
|
<Link href="/registers" className="btn btn-secondary">← Back to Registers</Link>
|
||||||
|
</div>
|
||||||
|
<RegisterDetailClient register={register} defaultActiveKey={defaultActiveKey} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -42,6 +42,8 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
name: regName,
|
name: regName,
|
||||||
description: description,
|
description: description,
|
||||||
notes: [],
|
notes: [],
|
||||||
|
text: "",
|
||||||
|
issue_4_only: false
|
||||||
};
|
};
|
||||||
|
|
||||||
const descriptionLines = description.split('\n');
|
const descriptionLines = description.split('\n');
|
||||||
@@ -49,28 +51,40 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
let accessData: RegisterAccess = { operations: [], notes: [] };
|
let accessData: RegisterAccess = { operations: [], notes: [] };
|
||||||
|
|
||||||
for (const line of descriptionLines) {
|
for (const line of descriptionLines) {
|
||||||
|
if(line.includes('Issue 4 Only')) reg.issue_4_only = true;
|
||||||
|
|
||||||
const trimmedLine = line.trim();
|
const trimmedLine = line.trim();
|
||||||
if (trimmedLine === '(R)') {
|
|
||||||
|
if (trimmedLine.startsWith('//')) continue;
|
||||||
|
|
||||||
|
if (trimmedLine.startsWith('(R)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'read';
|
currentAccess = 'read';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine === '(W)') {
|
if (trimmedLine.startsWith('(W)')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'write';
|
currentAccess = 'write';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (trimmedLine === '(R/W)') {
|
if (trimmedLine.startsWith('(R/W')) {
|
||||||
if (currentAccess) reg[currentAccess] = accessData;
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
accessData = { operations: [], notes: [] };
|
accessData = { operations: [], notes: [] };
|
||||||
currentAccess = 'common';
|
currentAccess = 'common';
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (line.startsWith(trimmedLine)) {
|
||||||
|
if (currentAccess) reg[currentAccess] = accessData;
|
||||||
|
accessData = { operations: [], notes: [] };
|
||||||
|
currentAccess = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentAccess) {
|
if (currentAccess) {
|
||||||
const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/);
|
const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/);
|
||||||
|
const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/);
|
||||||
|
|
||||||
if (bitMatch) {
|
if (bitMatch) {
|
||||||
let bitDescription = bitMatch[3];
|
let bitDescription = bitMatch[3];
|
||||||
const footnoteMatch = bitDescription.match(/(\*+)$/);
|
const footnoteMatch = bitDescription.match(/(\*+)$/);
|
||||||
@@ -84,6 +98,11 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
description: bitDescription,
|
description: bitDescription,
|
||||||
footnoteRef: footnoteRef,
|
footnoteRef: footnoteRef,
|
||||||
});
|
});
|
||||||
|
} else if (valueMatch) {
|
||||||
|
accessData.operations.push({
|
||||||
|
bits: valueMatch[1].trim().replace(/\s/g, ''),
|
||||||
|
description: valueMatch[2].trim(),
|
||||||
|
});
|
||||||
} else if (trimmedLine.startsWith('*')) {
|
} else if (trimmedLine.startsWith('*')) {
|
||||||
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/);
|
||||||
if (noteMatch) {
|
if (noteMatch) {
|
||||||
@@ -92,10 +111,16 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
text: noteMatch[2],
|
text: noteMatch[2],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if(trimmedLine) {
|
} else if (trimmedLine) {
|
||||||
if(accessData.operations.length > 0) {
|
if (line.match(/^\s+/) && accessData.operations.length > 0) {
|
||||||
accessData.operations[accessData.operations.length-1].description += `\n${trimmedLine}`;
|
accessData.operations[accessData.operations.length - 1].description += `\n${line}`;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!accessData.description) {
|
||||||
|
accessData.description = '';
|
||||||
}
|
}
|
||||||
|
accessData.description += `\n${trimmedLine}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (trimmedLine.startsWith('*')) {
|
if (trimmedLine.startsWith('*')) {
|
||||||
@@ -107,6 +132,9 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
reg.text += `${line}\n`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentAccess) {
|
if (currentAccess) {
|
||||||
@@ -130,18 +158,17 @@ function processRegisterBlock(paragraph: string, registers: Register[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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_records.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 (
|
||||||
<Container fluid className="py-4">
|
<div className="container-fluid py-4">
|
||||||
<h1 className="mb-4">Spectrum Next Registers</h1>
|
<h1 className="mb-4">Spectrum Next Registers</h1>
|
||||||
<RegisterBrowser registers={registers} />
|
<RegisterBrowser registers={registers} />
|
||||||
</Container>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface RegisterAccess {
|
export interface RegisterAccess {
|
||||||
|
description?: string;
|
||||||
operations: BitwiseOperation[];
|
operations: BitwiseOperation[];
|
||||||
notes: Note[];
|
notes: Note[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Register {
|
export interface Register {
|
||||||
hex_address: string;
|
hex_address: string;
|
||||||
dec_address: number | string;
|
dec_address: number | string;
|
||||||
@@ -23,5 +23,7 @@
|
|||||||
read?: RegisterAccess;
|
read?: RegisterAccess;
|
||||||
write?: RegisterAccess;
|
write?: RegisterAccess;
|
||||||
common?: RegisterAccess;
|
common?: RegisterAccess;
|
||||||
|
text: string;
|
||||||
notes: Note[];
|
notes: Note[];
|
||||||
|
issue_4_only: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
0
src/app/utils/parser.ts
Normal file
0
src/app/utils/parser.ts
Normal file
12
src/middleware.js
Normal file
12
src/middleware.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { NextResponse } from 'next/server'
|
||||||
|
|
||||||
|
export function middleware(request) {
|
||||||
|
const { method, nextUrl } = request
|
||||||
|
|
||||||
|
// Filter out internal Next.js assets if desired
|
||||||
|
if (!nextUrl.pathname.startsWith('/_next')) {
|
||||||
|
console.log(`${method} ${nextUrl.pathname}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user