Take a device's line down when its status port is read
A device raises a line and something has to take it down. Two things did: being interrupted, and being woken from WAIT with the Interrupt Flag down - the second because a masked program has nowhere to dispatch to, so nobody else would. There was a third way to learn a device had finished and nothing answered it. The documented idiom reads the status, branches out if the device is already done, and only WAITs otherwise; on a disk quick enough to finish before the first look, which is every disk here, the WAIT is unreachable. The line then stood for the rest of the machine's life. The program that leaves it standing never pays for it - it was masked throughout. The bill arrives at whoever next sets the Interrupt Flag. The boot chain reads the disk to load a program, leaves the line up, and hands over; the loaded program is then interrupted on behalf of a read that finished before it existed, through a vector table with no entry for a device it never touched, and faults on the instruction after its SIF. Found by running Examples/tune.asm through Once. It set up its whole sound and died four bytes before its first note, which is why it was silent rather than wrong - and why it looked like a sound bug for a while. So reading the port that answers a device takes its line down, the same way taking the byte already took the console's down. Disk and screen do it on their status port. And a reset now clears every line, which is the sentence the manual already makes about the vector table: a handler left behind aims an interrupt into a program that is no longer running, and so does a line. testPrograms/diskLineTest.asm pins it - the racy idiom, then SIF with no handler installed anywhere. It faults without the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
62a657f1a7
commit
85329f13c3
+22
-1
@@ -709,6 +709,10 @@ void clearInterrupt(uint8_t port) {
|
||||
linesClear(&machineLines, port);
|
||||
}
|
||||
|
||||
void clearAllInterrupts(void) {
|
||||
memset(&machineLines, 0, sizeof(machineLines));
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1156,7 +1160,24 @@ uint8_t InputHandler(uint8_t Address) {
|
||||
break;
|
||||
case DISK_BLOCK_HIGH: return (uint8_t)(diskBlock >> 8);
|
||||
case DISK_BLOCK_LOW: return (uint8_t)(diskBlock & 0xFF);
|
||||
case DISK_STATUS: return diskStatus;
|
||||
case DISK_STATUS:
|
||||
// ---- Looking is what answers it ----
|
||||
//
|
||||
// The console takes its line down when the byte is read, because taking the byte
|
||||
// is what answers the console. The disk's answer is this port: the operation
|
||||
// finished, and whether it worked is what Status is for. So reading it takes the
|
||||
// line down, the same way.
|
||||
//
|
||||
// WITHOUT THIS THE ORDINARY IDIOM LEAVES A LINE STANDING. The documented shape of
|
||||
// waiting for a device reads the status, branches out if the device is already
|
||||
// done, and only WAITs otherwise - so on a disk fast enough to finish before the
|
||||
// first look, which is every disk here, the WAIT that would have taken the line
|
||||
// down is never reached. Nothing else was going to answer it either: the program
|
||||
// is masked and has no handler. The line then stands for the rest of the
|
||||
// machine's life, and the next program to set the Interrupt Flag is interrupted
|
||||
// on behalf of a read that finished before it was loaded.
|
||||
clearInterrupt(PORT_DISK);
|
||||
return diskStatus;
|
||||
case PORT_REFUSE:
|
||||
// Refuses reads as well, so both directions are covered.
|
||||
refuseAccess(VECTOR_GUARD_VIOLATION);
|
||||
|
||||
@@ -358,6 +358,12 @@ void raiseInterrupt(uint8_t port);
|
||||
|
||||
void clearInterrupt(uint8_t port);
|
||||
|
||||
// Every line down at once, for a reset. The vector table is cleared when the machine starts
|
||||
// over because a handler left behind would aim an interrupt into a program that is no longer
|
||||
// running; a LINE left behind does exactly the same thing, and arrives at a program that
|
||||
// never asked the device for anything.
|
||||
void clearAllInterrupts(void);
|
||||
|
||||
// The lowest numbered port with its line up, or -1 if none of them are.
|
||||
int nextPendingInterrupt(void);
|
||||
|
||||
|
||||
@@ -137,6 +137,14 @@ static int machineRestart(Machine *m) {
|
||||
soundReset();
|
||||
consoleHome();
|
||||
consoleResetInput();
|
||||
// ---- And every line down ----
|
||||
//
|
||||
// The same reasoning that clears the vector table. A handler left behind would aim an
|
||||
// interrupt into a program that is no longer running; a line left behind arrives at one
|
||||
// that never asked the device for anything. The devices reset above take their own down,
|
||||
// and this is the rest of them - the disk in particular, which is not unplugged by a
|
||||
// reset and keeps whatever it was doing.
|
||||
clearAllInterrupts();
|
||||
initializeCPU(&m->cpu, Program, Data);
|
||||
// A machine that had stopped is running again, which is the entire point of asking from
|
||||
// outside: the interesting time to restart something is when it is not going anywhere.
|
||||
|
||||
@@ -223,7 +223,12 @@ uint8_t videoRead(uint8_t port) {
|
||||
// Looking is what answers it. A frame that has been noticed is not still
|
||||
// waiting to be, and a program polling in a loop would otherwise see the first
|
||||
// frame for ever.
|
||||
//
|
||||
// The line goes with the flag, and for the stronger reason: a program that polls
|
||||
// this port is not going to be the one that answers an interrupt, so a line left
|
||||
// standing here is one nothing will ever take down.
|
||||
frameWaiting = 0;
|
||||
clearInterrupt(PORT_VIDEO);
|
||||
return status;
|
||||
}
|
||||
case VIDEO_CONTROL:
|
||||
|
||||
Reference in New Issue
Block a user