Somewhere to send the fault about there being nowhere to send it
Dispatching through a vector with nothing in it was the one fault this machine could not hand over, because the thing that would hand it over is the thing that has just found nothing to hand it to. It stopped the machine and no program could do anything about it - so calling a service the system does not implement was fatal, and that is an ordinary mistake to make. Two new fault vectors: 5 when a software vector was empty, 6 when a device interrupted and its hardware entry was. Separate, because they are separate mistakes with separate fixes - one is a program calling something that is not there, the other a program that asked to be interrupted and forgot the handler. WHICH ENTRY WAS EMPTY ARRIVES IN Q, and it is the only thing on this machine a handler is given in a register. Not a fault cause register by another route: the vector still says what happened and Q says which of the 256 entries it happened about, which is a parameter and not a cause. It costs no new state at all, because the frame already saved the Q the interrupted program had and RETI puts it back. The escalation happens once. If vector 5 or 6 is itself empty the machine stops the way it always did, having genuinely run out of places to go. swiFaultTest is what guards that, and it was written long before any of this: it installs nothing, so it must still get the old halt. Breaking the escalation fails the two new tests and not that one; making the escalation unbounded fails that one and not the two new ones. Each break fails exactly the half it belongs to. noDeviceTest is fed no input on purpose. The console raises its line once when input ENDS as well as when a byte arrives - which exists so a program driven by interrupts is told when nothing more is coming - so with no input at all, that end is what turns up. Groundwork for CosmOS's fault screen, which wanted to catch these two and could not. 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
4d976fc22a
commit
000a6d39cb
@@ -547,7 +547,17 @@ If a device interrupts and its vector is empty, that is a fault: the machine sto
|
||||
|
||||
If the CPU reads a byte from Program Memory that does not decode to an instruction, it dispatches through Software Vector 2.
|
||||
|
||||
Faults get a vector each rather than sharing one. Vector 2 is the only cause defined so far, and vectors 3 through 15 are held back for the ones that come later, so that a handler always knows what happened from the entry it arrived through. That is why the machine has no fault cause register to read.
|
||||
Faults get a vector each rather than sharing one, so that a handler always knows what happened from the entry it arrived through. That is why the machine has no fault cause register to read.
|
||||
|
||||
| Vector | What happened |
|
||||
| --- | --- |
|
||||
| 2 | A byte in Program Memory does not decode to an instruction. |
|
||||
| 3 | A device refused a write that landed inside a raised fence. |
|
||||
| 4 | A bank was named that has nothing in it, or an access ran past its end. |
|
||||
| 5 | A software vector was dispatched through and had no handler. |
|
||||
| 6 | A device interrupted and its hardware vector had no handler. |
|
||||
|
||||
Vectors 7 through 15 are held back for the causes that come later.
|
||||
|
||||
The address in the frame is the address of the offending byte itself, not the one after it. A handler can therefore read the byte that failed and say what it was. It also means a handler that returns with a bare RETI will meet the same byte again, because resuming past a fault means deciding where to resume, and only the handler knows that.
|
||||
|
||||
@@ -555,6 +565,18 @@ If nothing is installed at Vector 2, the CPU sets the Fault Flag and the Halt Fl
|
||||
|
||||
Stopping matters because the alternative is worse. A byte that means nothing is almost always a sign that execution has wandered into data, or that a program was built for a machine with instructions this one does not have. Stepping over it and carrying on turns a clear failure into a program that appears to run and quietly does the wrong thing.
|
||||
|
||||
### Nowhere To Go:
|
||||
|
||||
Vectors 5 and 6 are the fault of dispatching through an empty entry, and they were a long time coming, because the thing that would hand that fault over is the thing which has just found nothing to hand it to. Until they existed, a `SWI` naming a service the system does not implement stopped the machine and no program could do anything about it - and calling a service that is not there is an ordinary mistake.
|
||||
|
||||
**Which entry was empty arrives in Q**, and it is the only thing on this machine a handler is given in a register. That is not a fault cause register by another route: the vector still says *what* happened, and Q says which of the 256 entries it happened about, which is a parameter and not a cause. It costs nothing, because the frame already saved the Q the interrupted program had and RETI puts it back.
|
||||
|
||||
The two are separate entries because they are separate mistakes with separate fixes. A missing software vector is usually a program calling something that is not there; a device with nobody listening is usually a program that asked to be interrupted and forgot the handler.
|
||||
|
||||
**The frame's address is past the SWI**, unlike every other fault here, because the instruction did dispatch - it was the entry that was empty. A bare RETI therefore carries straight on, where a bad opcode or a refused port would meet the same instruction again.
|
||||
|
||||
If Vector 5 or 6 is *itself* empty, the machine stops the way it always did. It has run out of places to go, and looping there would be worse than stopping. A handler that commits the same fault it was called about recurses like any other, which is the same bargain as a Vector 2 handler containing a byte that does not decode.
|
||||
|
||||
## Refusing:
|
||||
|
||||
A device can refuse what it was asked to do. This is not the same as interrupting. An interrupt is a device asking for attention later, answered between instructions once the CPU is ready. A refusal is a device saying no to the instruction happening now, so the machine stops where it stands rather than carrying on as though the access had worked.
|
||||
|
||||
Reference in New Issue
Block a user