Give the screen a bitmap mode

V4. Mode 2 is 320 by 200 with a byte a pixel: no tile to look up and no attribute to add,
the byte IS the palette index. Programs/Examples/picture.asm fills a whole one in 127 bytes
of program and 47,498 cycles.

IT IS THE SAME MEMORY AS THE TILES AND THE MAP, which is what shared video memory has always
been, and there is nowhere else it could be - 64,000 bytes of picture in a 65,536 byte bank
leaves room for nothing beside it. Going to bitmap mode does not clear the text screen, it
stops calling it one, and coming back finds the tiles holding whatever the picture put there.
Taking the screen means taking it.

The palette moves to 0xFC00, the top of video memory, because it is the one thing that has to
mean the same in every mode and 64,000 bytes of picture leaves nowhere in the middle for it
to hide. That is a documented address, so the example, the tests and the manual move with it.

A BITMAP HAS NO COLUMNS AND NO ROWS, and both registers read zero rather than a leftover from
the last mode. The console asks: told there is no character screen, it has nowhere to put a
glyph and draws nothing, while still saying everything down the serial line. The honest
alternative is what a machine with shared video memory really does, which is scribble marks
nobody can read across somebody's picture - honest and useless, since a program that has
taken the screen has not stopped wanting to print.

Six checks in Tests/video.sh, to 55: that the mode is 320 by 200, that a byte is one pixel's
colour and only that pixel, that printing leaves a picture alone while the letter still goes
out, and that the columns register says nought and then forty again.

The example is worth reading for one thing beyond the mode: Fill leaves its destination past
what it touched, so two hundred rows are drawn from one address set once. Working out where
row n begins would be n times 320, and this machine has no multiply.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-29 10:44:15 -04:00
co-authored by Claude Opus 5
parent 1174bd9af5
commit 13b20c8834
10 changed files with 258 additions and 20 deletions
+3 -3
View File
@@ -89,9 +89,9 @@ nextBank:
INIA 0x03
OUTA 0xE8 ; Command: RegisterBank
; The palette starts at 0xC000, and entry n is at n times four. Bank 2's ink is entry
; 2 * 16 + 1, which is 33, and 33 * 4 is 132 - so 0xC084.
INIA 0xC0
; The palette sits at the top of video memory, at 0xFC00, and entry n is at n times
; four. Bank 2's ink is entry 2 * 16 + 1, which is 33, and 33 * 4 is 132 - so 0xFC84.
INIA 0xFC
OUTA 0xE4 ; DestHigh
INIA 0x84
OUTA 0xE5 ; DestLow
+105
View File
@@ -0,0 +1,105 @@
; picture.asm
; The other kind of screen: a byte a pixel.
; Written by Anachronaut
;
; ---- Two ways to have a screen ----
;
; A tile mode costs the machine the number of CELLS that changed. Forty by twenty-five is
; two thousand bytes for a whole screen and four bytes for two cells, which is why text on
; this machine is affordable at all.
;
; A bitmap costs it the number of PIXELS. Three hundred and twenty by two hundred is 64,000
; bytes - four frames of work at a megahertz to replace all of it. So this is not the mode to
; animate a whole screen in; it is the mode to draw a picture in and then leave alone, or to
; change a corner of.
;
; It lives over the top of tile memory and the map, because there is nowhere else for it: the
; bank is 65,536 bytes and the picture is 64,000 of them. Going to bitmap mode does not clear
; the text screen, it stops calling it one - and coming back finds the tiles holding whatever
; the picture put there. Taking the screen means taking it.
;
; The palette is the one thing that means the same in both, which is why it sits at the very
; top, out of the way of everything.
#Program
start:
; Video memory is the screen's, not this program's, so it is reached the way every device's
; memory is: given a bank number, then written through the memory controller. Banks 0, 1
; and 2 belong to the machine, so 3 is the first one software may hand out.
INIA 0d3
OUTA 0xE3 ; DestBank: the number being given
INIA 0x30
OUTA 0xE2 ; SourceLow: the port that owns the memory
INIA 0x03
OUTA 0xE8 ; Command: RegisterBank
; ---- Two hundred and fifty six colours ----
;
; Entry n at 0xFC00 plus n times four. Writing the controller's Data port puts a byte at
; the destination and steps it on, so the whole palette is one address and a loop.
INIA 0xFC
OUTA 0xE4
RSTA
OUTA 0xE5
SETD.0 Count
STA.0 ; Still the zero from DestLow above: SETD does not touch A
palette:
LDA.0
OUTA 0xE9 ; red climbs
LDA.0
OUTA 0xE9 ; green with it
LDA.0
INIB 0xFF
XOR
MVQA
OUTA 0xE9 ; and blue falls away, so it runs blue to white to yellow
RSTA
OUTA 0xE9 ; the fourth byte is spare
LDA.0
INCA
STA.0
BNA palette ; A comes back to zero after 256 of them
; ---- The picture ----
;
; Two hundred rows of three hundred and twenty pixels, each row one colour. FILL LEAVES THE
; DESTINATION PAST WHAT IT TOUCHED, so the address is set once here and never worked out
; again - which matters, because working out where row n begins would be n times 320 and
; this machine has no multiply.
RSTA
OUTA 0xE4
OUTA 0xE5 ; Dest 0x0000, the top left corner
INIA 0x01
OUTA 0xE6
INIA 0x40
OUTA 0xE7 ; 320 bytes, which is one row
SETD.0 Count
RSTA
STA.0
rows:
LDA.0
OUTA 0xE2 ; Fill takes its byte from SourceLow: the row number is the colour
INIA 0x02
OUTA 0xE8 ; Command: Fill
LDA.0
INCA
STA.0
INIB 0d200
CCF
SUB
BNQ rows
; And now show it. Nothing above cared which mode the screen was in - the bytes were
; already there, waiting to be called a picture.
INIA 0x02
OUTA 0x31
HALT
#Data
Count:
0x00
#Vectors
Boot start