Give the console colour and a cursor
COLOUR COSTS A NIBBLE AND NO HARDWARE. A glyph is drawn in palette indices 0 and 1, paper and ink, and a cell's attribute nibble adds sixteen to both - so sixteen banks is already sixteen ink and paper pairs, and all that was missing was a register saying which one the console draws in. That is port 0x06, read as well as written like the rest. The palette a machine wakes up with is arranged so that HIGHLIGHTING IS ONE BIT: banks 0 to 7 are colours on black, banks 8 to 15 are the same colours as paper with black ink. So attribute XOR 8 turns any pair inside out. That is a convention rather than a rule of the machine - the device only ever adds the nibble and looks the answer up - but it is the convention that makes a highlighted line and a cursor free. Bank 0 is still grey on black, so nothing that was written before this has changed colour. THE CURSOR IS THE SAME BIT AGAIN. It is drawn by turning its cell inside out rather than by putting a block over it, so the character underneath stays readable, which matters to somebody editing a line. The device draws it rather than the window, because on a machine with a screen a cursor is a hardware feature - one drawn by the presenter would not be in a picture the machine saved. It blinks on the machine's own clock, half a second each way, so the phase is a pure function of the cycle count and a screen saved at a given cycle is the same screen every time. A blink on the host's clock would have made every saved picture a matter of luck. Off unless asked for, with bit 2 of the control port. That is right for a machine - a program painting its own screen does not want something blinking in the middle of it - and CosmOS asks for one at boot. It also asks again when it takes the console back from a program that has stopped, because a program handing key mode back the way it was told to writes zero, which turns the cursor off. The shell owns the prompt, so the shell is what makes sure there is something blinking at it. Nine more checks in Tests/video.sh, to 41: that the attribute colours the ink and not the paper, that XOR 8 turns both, that it reads back, that a cursor appears where the registers put it and only when asked for, and that it goes dark again half a million cycles later. 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
978aec4809
commit
d6feddd1b6
@@ -93,6 +93,13 @@ emit() {
|
||||
printf ' INIA 0d%d\n OUTA 0x00\n' "$1"
|
||||
}
|
||||
|
||||
# About 262,000 cycles of nothing. A DECA is one byte and a BNA is three, so four cycles a
|
||||
# turn, 256 times 256. The label suffix is so that two of these can sit in one program.
|
||||
spin() {
|
||||
printf ' RSTB\nspinOuter%s:\n RSTA\nspinInner%s:\n DECA\n BNA spinInner%s\n DECB\n BNB spinOuter%s\n' \
|
||||
"$1" "$1" "$1" "$1"
|
||||
}
|
||||
|
||||
# A string to the console, which is all a program has ever had to do to put text on a
|
||||
# SplitBit. That it now appears on a screen is the whole of this rung.
|
||||
say() {
|
||||
@@ -410,6 +417,67 @@ GOT="$(said cursorclamp)"
|
||||
&& result ok "a cursor past the edge is clamped" "column 39, the last one" \
|
||||
|| result no "a cursor past the edge is clamped" "got $GOT"
|
||||
|
||||
# ---- Colour, which costs a nibble and no hardware ----
|
||||
#
|
||||
# A glyph is drawn in palette indices 0 and 1, paper and ink, and a cell's attribute nibble
|
||||
# adds sixteen to both. Sixteen banks is therefore sixteen ink and paper pairs, and the
|
||||
# default palette is arranged so that XOR 8 turns any of them inside out.
|
||||
coloured() { [ "$(pixel "$1" "$2" "$3")" = "$4" ]; }
|
||||
|
||||
{ printf '#Program\nstart:\n'; port 0x06 0x01; say "A"; epilogue; } | run inkred || exit 1
|
||||
coloured inkred 2 1 "208,64,56" \
|
||||
&& result ok "the attribute register colours the ink" "bank 1 is red on black" \
|
||||
|| result no "the attribute register colours the ink" "got $(pixel inkred 2 1)"
|
||||
coloured inkred 0 0 "0,0,0" \
|
||||
&& result ok "and leaves the paper alone" "still black behind it" \
|
||||
|| result no "and leaves the paper alone" "got $(pixel inkred 0 0)"
|
||||
|
||||
# The same colour with one bit more, which is the whole of highlighting.
|
||||
{ printf '#Program\nstart:\n'; port 0x06 0x09; say "A"; epilogue; } | run highlight || exit 1
|
||||
coloured highlight 0 0 "208,64,56" \
|
||||
&& result ok "XOR 8 turns a pair inside out" "red paper now" \
|
||||
|| result no "XOR 8 turns a pair inside out" "got $(pixel highlight 0 0)"
|
||||
coloured highlight 2 1 "0,0,0" \
|
||||
&& result ok "and the ink with it" "black letters on it" \
|
||||
|| result no "and the ink with it" "got $(pixel highlight 2 1)"
|
||||
|
||||
# Readable, like every other console register.
|
||||
{ printf '#Program\nstart:\n'; port 0x06 0x05; show 0x06; epilogue; } | run attrread || exit 1
|
||||
[ "$(said attrread)" = "5" ] \
|
||||
&& result ok "and the attribute reads back" "bank 5" \
|
||||
|| result no "and the attribute reads back" "got $(said attrread)"
|
||||
|
||||
# ---- The cursor ----
|
||||
#
|
||||
# Drawn by the device, turned inside out rather than drawn over, so that a person editing a
|
||||
# line can still see the character they are standing on. Off unless asked for: a program
|
||||
# painting its own screen does not want one blinking in the middle of it.
|
||||
{ printf '#Program\nstart:\n'; port 0x02 0x04; epilogue; } | run cursoron || exit 1
|
||||
coloured cursoron 0 0 "216,216,216" \
|
||||
&& result ok "a cursor appears where the console is" "an empty cell, inside out" \
|
||||
|| result no "a cursor appears where the console is" "got $(pixel cursoron 0 0)"
|
||||
|
||||
{ printf '#Program\nstart:\n'; epilogue; } | run cursoroff || exit 1
|
||||
coloured cursoroff 0 0 "0,0,0" \
|
||||
&& result ok "and there is none unless asked for" "the machine draws what it is told" \
|
||||
|| result no "and there is none unless asked for" "got $(pixel cursoroff 0 0)"
|
||||
|
||||
{ printf '#Program\nstart:\n'; port 0x02 0x04; port 0x03 0x03; port 0x04 0x07; epilogue
|
||||
} | run cursorwhere || exit 1
|
||||
coloured cursorwhere 56 24 "216,216,216" \
|
||||
&& result ok "and it follows the cursor registers" "row 3, column 7" \
|
||||
|| result no "and it follows the cursor registers" "got $(pixel cursorwhere 56 24)"
|
||||
|
||||
# ---- And it blinks on the machine's own clock ----
|
||||
#
|
||||
# Which is what makes it deterministic: the phase is a pure function of the cycle count, so
|
||||
# a screen saved at a given cycle is the same screen every time. Half a million cycles in it
|
||||
# is dark, and this burns about 524,000 - a DECA and a BNA are four cycles a turn.
|
||||
{ printf '#Program\nstart:\n'; port 0x02 0x04; spin a; spin b; epilogue; } | run cursorblink || exit 1
|
||||
coloured cursorblink 0 0 "0,0,0" \
|
||||
&& result ok "the cursor blinks off again" "half a second later, dark" \
|
||||
|| result no "the cursor blinks off again" "got $(pixel cursorblink 0 0)"
|
||||
|
||||
# And what scrolled off the top is still in the map, which is scrollback nothing had to keep.
|
||||
{ printf '#Program\nstart:\n'
|
||||
say "A"
|
||||
|
||||
Reference in New Issue
Block a user