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
106 lines
3.3 KiB
NASM
106 lines
3.3 KiB
NASM
; 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
|