Tell the person where it hurts

A fault stopped the machine and printed a line to standard error. On a terminal
that is a diagnosis. Behind a window it is a frozen picture and no reason at
all, because the message went somewhere nobody was looking - the machine looked
hung and was not. It had stopped, and said so invisibly.

CosmOS catches all five faults now and says what happened on the screen, with
the address, in red.

A FAULT ENDS THE PROGRAM, NOT THE MACHINE. That is the answer to "carry on or
start again", and it is not a compromise: a bare RETI from most of these meets
the instruction that failed and fails again, so carrying on was never on offer.
But the machine is almost never what is broken. Everything the shell puts back
when a program exits - the Stack, its vectors, the drive, the working directory,
the console, the screen - is exactly what wants putting back after one dies, so
the handler sets a status and joins handleExit. You are back at the prompt, and
the program is recorded as having STOPPED rather than finished, because saying
"finished" under a red fault message would be the shell contradicting itself.

A fault below where programs load is the system's own, and there is nothing to
go back to. That one says so and stops.

THE SCREEN GOES BACK TO A MODE TEXT CAN BE SEEN IN, and that is the part that
matters rather than the part that is prettiest. A program that faulted in bitmap
mode left the console with no text rows, so it draws nothing at all: the message
would be perfectly correct and completely invisible, which is the one thing it
must never be. Two palette entries go back for the same reason, since a program
that wrote its own colours can leave every ink the same as every paper. Only the
two the message needs, so the rest of what the program chose is left alone.

Both halves are checked by looking at the PICTURE, because the serial line was
never where the problem was. Crash blind ruins the palette and drops into bitmap
mode before it faults; without the mode the screen comes back 320 by 200 with
nothing on it, and without the palette it is the right size with the message
present and unreadable. Each break loses the red on its own.

Crash is also a program worth having: it breaks in whichever of the five ways
you name, so a fault screen can be looked at without having written a bug first.

Two things found on the way:

The native assembler keeps its OWN copy of the reserved vector names, so it did
not know NoHandler or NoDevice and built a cosmos.bin that differed from the
host assembler's. Caught by native.sh, which is exactly the drift that test
exists for.

And cosmosMonitor had dead input. It assembles code into 0x8000 and runs it, and
that code faults - which used to kill the machine, so everything after it in the
file had never run. It runs now, and the recording grew by sixty lines of
monitor session that had been unreachable since the day the fault was put there.

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 15:08:20 -04:00
co-authored by Claude Opus 5
parent 000a6d39cb
commit fd9c4c75f8
19 changed files with 931 additions and 10 deletions
+42
View File
@@ -911,6 +911,48 @@ inked scrollback 2 1 \
&& result ok "what scrolled off is still there" "the origin went back and found it" \
|| result no "what scrolled off is still there" "nothing at 2,1"
# ---- The fault screen, from a screen with nowhere to print on it ----
#
# Every other test of the fault screen reads what came down the serial line, and the serial
# line is not where the problem was: a program that faulted in BITMAP MODE left the console
# with no text rows, so it drew nothing at all, and the machine looked hung while it was
# merely unable to say so. What has to be checked is the PICTURE.
#
# Two things about it, and both matter. The picture is 640 by 400, which is the eighty column
# text mode - so the screen really was put back, from a mode that has no characters in it.
# And it holds the fault red, which says the message was drawn and drawn in a colour that can
# be read whatever palette the program had left behind.
python3 -c "open('$BUILD/blind.keys','wb').write(b'Crash blind\n' + b'\x00'*400)"
timeout 30 "$EMU" --fast --cycles 8000000 --keyboard "$BUILD/blind.keys" \
--screen "$BUILD/blind.ppm" --disk "$ROOT/Tests/build/disks/cosmos.img" \
"$BUILD/cosmos.bin" > "$BUILD/blind.out" 2>&1 || true
if [ -f "$BUILD/blind.ppm" ]; then
SIZE="$(head -c 20 "$BUILD/blind.ppm" | sed -n '2p')"
[ "$SIZE" = "640 400" ] \
&& result ok "a fault puts the screen back where text can be seen" "eighty columns again, from bitmap mode" \
|| result no "a fault puts the screen back where text can be seen" "the picture is $SIZE"
REDDISH="$(python3 - "$BUILD/blind.ppm" <<'PY2'
import sys
data = open(sys.argv[1], "rb").read()
parts = data.split(b"\n", 3)
pixels = parts[3]
# The red the machine wakes up with, which is the one the fault screen writes into the
# entries attribute one draws from. Counted rather than looked for once, so a single stray
# pixel of it could not pass for a message.
red = sum(1 for i in range(0, len(pixels) - 2, 3)
if (pixels[i], pixels[i + 1], pixels[i + 2]) == (0xD0, 0x40, 0x38))
print("yes" if red > 200 else "only %d red pixels" % red)
PY2
)"
[ "$REDDISH" = "yes" ] \
&& result ok "and says what happened in a colour that can be read" "the message is drawn in the fault red" \
|| result no "and says what happened in a colour that can be read" "$REDDISH"
else
result no "a fault puts the screen back where text can be seen" "no picture was written"
fi
echo
if [ "$FAIL" -eq 0 ]; then
echo "All $PASS video checks passed."