diff --git a/Source/Emulator/io.c b/Source/Emulator/io.c index 309b276..a869df3 100644 --- a/Source/Emulator/io.c +++ b/Source/Emulator/io.c @@ -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 diff --git a/Source/Emulator/io.h b/Source/Emulator/io.h index 3090bed..fc0dfeb 100644 --- a/Source/Emulator/io.h +++ b/Source/Emulator/io.h @@ -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);