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
@@ -204,6 +204,9 @@ waitForDisk:
|
||||
ready:
|
||||
```
|
||||
|
||||
Notice that on a device quick enough to finish before the first look, the `WAIT` in that loop
|
||||
never runs at all. That is fine, and it is why the next section exists.
|
||||
|
||||
Test the device, then wait. If the device finishes in the gap between the two, its line is
|
||||
standing when WAIT runs and the wait is skipped rather than slept through.
|
||||
|
||||
@@ -390,6 +393,40 @@ One last thing. An interrupt arriving while the Stack Pointer is somewhere unusu
|
||||
|
||||
# When Something Else Wants Attention
|
||||
|
||||
## Answering A Line:
|
||||
|
||||
A device raises its line when it has something to say, and something has to take it down
|
||||
again. There are three things that do, and between them they cover every way a program can
|
||||
find out that a device is finished.
|
||||
|
||||
**Being interrupted takes it down.** The dispatch does it, before the handler runs, which is
|
||||
why a handler does not have to and why a handler that forgets does not spin.
|
||||
|
||||
**Being woken from `WAIT` with the Interrupt Flag down takes it down**, because nobody else is
|
||||
going to. A masked program has nowhere to dispatch to, and a line left standing would be found
|
||||
by the next `WAIT`, and the one after that, and the program would spin exactly as it did
|
||||
before while appearing to sleep.
|
||||
|
||||
**And reading the port that answers the device takes it down.** For the console that is the
|
||||
data port, because taking the byte is what answers the console. For the disk and the screen it
|
||||
is the status port: the operation finished, and whether it worked is what Status is for.
|
||||
|
||||
That third one is the one to have in mind, because without it the loop above has a hole in it.
|
||||
A program that polls, finds the device already done and never reaches its `WAIT` has used none
|
||||
of the first two. The line stands - and it stands for the rest of the machine's life, because
|
||||
nothing is ever going to come along and answer it.
|
||||
|
||||
**What that costs is not paid by the program that leaves it.** That program never set the
|
||||
Interrupt Flag; it was masked throughout. The bill arrives later, at whoever does. The boot
|
||||
chain reads the disk to load a program, leaves the line up, and hands over - and the loaded
|
||||
program is interrupted on behalf of a read that finished before it existed, through a vector
|
||||
table that has no entry for a device it never touched. It faults on the instruction after its
|
||||
`SIF`. `Programs/Examples/tune.asm` is how this was found: run through `Once`, it set up its
|
||||
whole sound and then died four bytes before playing a note.
|
||||
|
||||
So: a status read is an acknowledgement, and a program that wants to be interrupted by a
|
||||
device should not poll it.
|
||||
|
||||
## Interrupts:
|
||||
|
||||
An interrupt is an involuntary transfer of control. A subroutine call is agreed to by the code that makes it, so CALL can leave Q and Data Pointer 3 alone and let a subroutine pass results back through them. An interrupt arrives in code that has never heard of it, where Q and DP3 are ordinary working registers, so it saves everything:
|
||||
|
||||
Reference in New Issue
Block a user