Give the CPU a bus, so that there can be more than one

The first piece of the peripheral core, and it changes no behaviour: the machine still has
exactly one processor, and every one of the 169 tests still passes. What changes is that the
code has stopped assuming so.

FIVE THINGS A CPU ASKS OF THE WORLD OUTSIDE ITSELF, and every one of them was a call to a
function there was exactly one of: the port handlers, what the controller spent moving memory,
what was spent stopped waiting on a device, and the two that work the interrupt lines. Fine
for a machine with one processor and wrong for a machine with two, because A PERIPHERAL CORE'S
BUS IS ITS OWN - it sees the devices its own device gives it, raises its own lines, and stalls
on its own controller, none of which are the host's.

They are gathered into a Bus that the CPU holds a pointer to, rather than threaded through as
a bus number, because a bus is something a device PROVIDES. A device that provides one should
hand over the answers rather than be looked up by an index somebody else has to keep right.
initializeCPU puts the machine's own there, which is what every processor was on when there
could only be one, so nothing at any call site moved.

And shiftRegister is a local now. It always was one in effect - written and read inside a
single instruction and never carried to the next - but it sat at file scope, where a second
processor would have shared it. Two cores taking each other's shift halfway through an SHL is
a poor thing to discover later, and it cost two braces to make impossible.

Still to come on this rung: the interrupt bitmap, which is one file-scope array indexed by
port for the whole machine; a controller each; and the interleaving rule, which has to be
written into the manual as machine behaviour rather than left as something the emulator does.

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:11:54 -04:00
co-authored by Claude Opus 5
parent f2e26c1852
commit dae3455da0
4 changed files with 75 additions and 14 deletions
+26
View File
@@ -51,6 +51,29 @@ typedef enum {
FAULT_DEVICE_REFUSED // A device refused, and nothing was installed to catch it.
} FaultCause;
// ---- What a CPU is plugged into ----
//
// Five things a CPU asks of the world outside itself, and every one of them was a call to a
// function there was exactly one of. That is fine for a machine with one processor and wrong
// for a machine with two: A PERIPHERAL CORE'S BUS IS ITS OWN. It sees the devices its own
// device gives it, raises its own interrupt lines, and stalls on its own controller - none
// of which are the host's.
//
// Gathered here rather than threaded through as a bus number, because a bus is a thing a
// device provides, and a device that provides one should hand over the answers rather than
// be looked up by an index somebody has to keep right.
typedef struct {
uint8_t (*out)(uint8_t value, uint8_t port);
uint8_t (*in)(uint8_t port);
// What the memory controller on this bus has just spent moving memory, and what the
// machine spent stopped waiting on a device. Both are taken and cleared.
unsigned long (*takeStall)(void);
unsigned long (*takeIdle)(void);
// The lowest port with its line up, or below zero for none, and putting one down.
int (*nextInterrupt)(void);
void (*clearInterrupt)(uint8_t port);
} Bus;
// The struct containing the CPU registers.
typedef struct {
uint8_t A;
@@ -62,6 +85,9 @@ typedef struct {
uint16_t StackPointer;
uint8_t *Program;
uint8_t *Data;
// Which bus this processor is on. initializeCPU puts the machine's own here, which is
// what every CPU had before there could be more than one.
const Bus *bus;
// ---- What the machine has cost so far ----
//