Make the interrupt lines belong to a bus rather than to the machine

The second piece of the peripheral core, and like the first it changes nothing: one array of
bits became a struct, and the machine's own devices reach it through wrappers because every
one of them really is on this bus.

IT WAS ONE ARRAY INDEXED BY PORT FOR THE WHOLE MACHINE. With two processors that is not a
tidiness problem, it is each of them seeing the other's lines: a disk finishing would
interrupt a sound core, a sound core's tick would interrupt the shell, and both would arrive
at a handler installed for something else entirely. Hardware vectors are per port, so the
numbers would even look plausible on the way in.

Unlike the shift register, which was harmless until there was a second core to share it with,
this one was always going to be wrong the moment there was one.

Still to come on this rung: a controller each, since it holds the source, the destination, the
length and five guard registers, and two cores setting those between each other's instructions
would interleave into nonsense. Then the interleaving rule, which belongs in the manual as
machine behaviour rather than as something the emulator happens to do.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-29 15:43:54 -04:00
co-authored by Claude Opus 5
parent 4e3258e3f7
commit 5be5bea994
2 changed files with 48 additions and 10 deletions
+23 -10
View File
@@ -622,11 +622,9 @@ static uint8_t consoleStatus(void) {
return status;
}
// One bit per port, so a device can ask for attention without anything having to poll
// it. Eight ports to the byte, low bit first.
#define INTERRUPT_LINE_BYTES 32
static uint8_t pendingInterrupts[INTERRUPT_LINE_BYTES];
// The machine's own lines. A peripheral core's device keeps its own set, which is the
// entire reason these are a struct rather than an array sitting here.
static InterruptLines machineLines;
// Whether somebody has asked the machine to start over, and taking that request away.
static int resetWanted = 0;
@@ -671,24 +669,35 @@ const Bus *machineBus(void) {
return &theMachinesBus;
}
void linesRaise(InterruptLines *lines, uint8_t port) {
lines->bits[port >> 3] |= (uint8_t)(1u << (port & 7));
}
void linesClear(InterruptLines *lines, uint8_t port) {
lines->bits[port >> 3] &= (uint8_t)~(1u << (port & 7));
}
// The machine's own, which is what every device in this file means when it asks for
// attention. Wrappers rather than a change at every call site, because every one of those
// devices really is on this bus and saying so twenty times would not make it truer.
void raiseInterrupt(uint8_t port) {
pendingInterrupts[port >> 3] |= (uint8_t)(1u << (port & 7));
linesRaise(&machineLines, port);
}
void clearInterrupt(uint8_t port) {
pendingInterrupts[port >> 3] &= (uint8_t)~(1u << (port & 7));
linesClear(&machineLines, port);
}
int nextPendingInterrupt(void) {
int linesNext(const InterruptLines *lines) {
// Lowest numbered port wins. This is a scan rather than a priority encoder, which
// means there is no arbitration to explain and a programmer can work out what
// happens next by reading the port numbers.
for (int group = 0; group < INTERRUPT_LINE_BYTES; group++) {
if (pendingInterrupts[group] == 0) {
if (lines->bits[group] == 0) {
continue;
}
for (int bit = 0; bit < 8; bit++) {
if (pendingInterrupts[group] & (1u << bit)) {
if (lines->bits[group] & (1u << bit)) {
return group * 8 + bit;
}
}
@@ -696,6 +705,10 @@ int nextPendingInterrupt(void) {
return -1;
}
int nextPendingInterrupt(void) {
return linesNext(&machineLines);
}
// ---- Refusing ----
//
// Set when a device will not do what it was asked, and read by the CPU immediately
+25
View File
@@ -323,6 +323,31 @@ void requestReset(void);
// left the keyboard dead would be a reset nobody could use twice.
void consoleResetInput(void);
// ---- The lines on one bus ----
//
// One bit a port, so a device can ask for attention without anything having to poll it.
// Eight ports to the byte, low bit first.
//
// A MACHINE WITH TWO PROCESSORS HAS TWO SETS OF THESE. A peripheral core's devices raise
// lines on the core's bus and the host's devices on the host's, and they are not the same
// lines: one array indexed by port for the whole machine would have let each see the other's,
// so a disk finishing would interrupt a sound core and a sound core's tick would interrupt
// the shell. Both would arrive at a handler for something else entirely.
#define INTERRUPT_LINE_BYTES 32
typedef struct {
uint8_t bits[INTERRUPT_LINE_BYTES];
} InterruptLines;
void linesRaise(InterruptLines *lines, uint8_t port);
void linesClear(InterruptLines *lines, uint8_t port);
// The lowest numbered port with its line up, or below zero if none has. A scan rather than
// a priority encoder, so there is no arbitration to explain and a programmer can work out
// what happens next by reading the port numbers.
int linesNext(const InterruptLines *lines);
// The machine's own lines, which is what every device on this bus means.
void raiseInterrupt(uint8_t port);
void clearInterrupt(uint8_t port);