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:
Anachronaut
2026-09-01 14:40:53 -04:00
co-authored by Claude Opus 5
parent 4d976fc22a
commit 000a6d39cb
11 changed files with 206 additions and 5 deletions
+25 -1
View File
@@ -66,7 +66,31 @@
#define VECTOR_GUARD_VIOLATION 3
// A bank was named that has nothing registered in it, or an access ran past its end.
#define VECTOR_BANK_FAULT 4
// Vectors 5 to 15 are held back for faults that do not exist yet, so that each cause
// ---- The two faults that used to be uncatchable ----
//
// A vector was dispatched through and had nothing in it. That is discovered by the very
// thing that would dispatch, so for a long time it could only stop the machine: there was
// no way to hand a program a fault about there being nowhere to hand it.
//
// These two are where it goes instead, and they are separate for the same reason every
// other cause is - a missing software vector and a device nobody is listening to are
// different mistakes with different fixes, and a handler should not have to work out which
// it was.
//
// WHICH ENTRY WAS EMPTY ARRIVES IN Q, and that is the only thing on this machine a handler
// is given in a register. It is not a cause register by the back door: the vector still
// says what happened, and Q says which of the 256 entries it happened about, which is a
// parameter rather than a cause. It costs no new state, because the frame already saved the
// Q the interrupted program had and RETI puts it back.
//
// A vector THESE dispatch through and find empty stops the machine, and has to: a machine
// that cannot report a fault about a missing handler by any route has run out of places to
// go, and looping there would be worse than stopping.
#define VECTOR_NO_HANDLER 5
#define VECTOR_NO_DEVICE 6
// Vectors 7 to 15 are held back for faults that do not exist yet, so that each cause
// can have an entry of its own rather than sharing one and needing a cause register to
// tell them apart. Everything from 16 up belongs to programs, in two halves.
//
+2
View File
@@ -226,6 +226,8 @@ static const struct {
{ "BadOpcode", VECTOR_INVALID_OPCODE },
{ "GuardViolation", VECTOR_GUARD_VIOLATION },
{ "BankFault", VECTOR_BANK_FAULT },
{ "NoHandler", VECTOR_NO_HANDLER },
{ "NoDevice", VECTOR_NO_DEVICE },
};
static const int reservedVectorCount = (int)(sizeof(reservedVectors) / sizeof(reservedVectors[0]));