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
+51
View File
@@ -0,0 +1,51 @@
; A device raising its line with nothing installed to answer it, CAUGHT.
;
; The other half of the fault that could not be handed over. A program that asks a device to
; interrupt it and then forgets the handler used to stop the machine dead; now it goes to a
; vector of its own with the port in Q, so the program can say which device it was that
; nobody was listening to.
;
; NOTHING IS TYPED AT THIS. The console raises its line once at the end of input as well as
; for an arriving byte - which is exactly so that a program driven by interrupts is told when
; nothing more is coming - and the test is run with no input at all, so that end is what
; arrives.
;
; Correct output is:
; nobody listening on port 00
; and a clean halt.
#Include console.asm
#Program
start:
INIA 0x02 ; Interrupt when the console has something to say.
OUTA 0x02
SIF
spin:
; Never touches the console. Whatever happens below is something that interrupted this.
BRI spin
; Q holds the port whose entry was empty. The console is port zero.
nobody:
SETD.0 NobodyText
CALL printString
MVQA
CALL printByteHex
CALL newLine
; The console back to how it was found, so nothing else is asked for, and stop. There is
; nothing to resume to: the loop above only exists to be interrupted.
RSTA
OUTA 0x02
HALT
#Data
NobodyText:
"nobody listening on port "
#Vectors
Boot start
NoDevice nobody
+56
View File
@@ -0,0 +1,56 @@
; A software interrupt that names a vector with nothing in it, CAUGHT.
;
; That fault used to be the one the machine had no way of handing 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 - which made calling a service the
; system does not implement fatal, and that is an ordinary mistake to make.
;
; It goes to a vector of its own now, with the number of the empty entry in Q. This catches
; it, says which one it was, steps over the SWI and its operand, and carries on - so the
; output says both that the fault arrived and that a program can survive it.
;
; Correct output is:
; empty vector 28
; carried on
; and a clean halt.
#Include console.asm
#Program
start:
SWI 0d40 ; Forty is 0x28, and nothing is installed there.
SETD.0 Carried
CALL printString
CALL newLine
HALT
; Entered because there was nowhere to go. Q holds which entry was empty, and it is the only
; thing on this machine a handler is handed in a register.
missing:
SETD.0 EmptyText
CALL printString
MVQA
CALL printByteHex
CALL newLine
; ---- Carrying on past it ----
;
; The frame holds the address after the SWI and the byte naming its vector, so a bare RETI
; already lands past the instruction that faulted. That is not true of every fault here -
; a refused port or a byte that does not decode both resume ON the thing that failed - so
; it is worth saying out loud which kind this one is.
RETI
#Data
EmptyText:
"empty vector "
Carried:
"carried on"
#Vectors
Boot start
NoHandler missing