The screen is two banks: an atlas and a screen

Tiles and colours are written when a program loads; the map is written
whenever anything moves. Sharing one 64K bank made them compete for room
neither needed all of, and had a worse consequence than being cramped: a
bitmap covers the whole bank, so entering bitmap mode destroyed the font.
A program could not draw a picture and then say anything about it.

Split, each gets a whole bank. The atlas holds the tiles and the palette,
the screen holds the map or a bitmap, and a picture now costs the map and
nothing else. It also leaves 48K free in the atlas, which is where the
sprite table and a second page of tiles are going.

No new mechanism was needed. A bank is registered by naming the port that
owns it, so a device with two banks needs two ports that own memory: the
base port keeps the atlas, since tiles have been at 0x0000 since there was
a screen at all, and 0x3A owns the screen. The registry now answers
honestly about which ports in the block bring memory, where it used to say
all sixteen did.

CosmOS never addresses video memory except in one place - the screen save,
which walks 196 pages of it. The page number already says which bank a page
is in, so screenBankFor works it out rather than keeping a second list
beside screenPageFor. Grid and picture.asm register both banks; colours.asm
only touches the palette and needed none of it.

Tests/video.sh names the memory every write is for, because an address
cannot: tile 5 and bitmap pixel 5 are both 0x0005, and a helper that
guessed would be right for the tiles and silently wrong for a picture.

And picture.asm gained a check, because this change broke it and nothing
noticed - registering the second bank leaves DestBank pointing at it, so
the palette went into the wrong one and the picture came out black. It was
the only thing here found by looking rather than by a test.

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-02 10:09:53 -04:00
co-authored by Claude Opus 5
parent 2abc8281df
commit 66e7b84272
17 changed files with 313 additions and 108 deletions
+14 -3
View File
@@ -37,12 +37,23 @@ start:
; fail - it succeeds, and the disk's buffer quietly becomes the screen. Every read the
; filesystem made after that came out of video memory, so the shell found an empty disk
; and could not start anything by name. Nothing said a word.
; TWO BANKS, because the screen has two. The atlas holds the tiles and the palette and is
; written when a program starts; the screen holds the map and is written as things move.
; They are separate memories, so a bank number reaches one or the other and never both -
; which is the whole of what this program had to learn when they were split apart.
INIA 0d4
OUTA 0xE3 ; DestBank: the number it will answer to.
INIA 0x30
OUTA 0xE2 ; SourceLow: the port of the device that owns it.
INIA 0x03
OUTA 0xE8 ; RegisterBank.
OUTA 0xE8 ; RegisterBank. Four is the atlas.
INIA 0d5
OUTA 0xE3
INIA 0x3A
OUTA 0xE2 ; And the port that owns the screen.
INIA 0x03
OUTA 0xE8 ; RegisterBank. Five is the map.
; ---- Asking for the screen back afterwards ----
;
@@ -153,7 +164,7 @@ finished:
OUTA 0x34
OUTA 0x36 ; The origins, or the shell looks at a corner of the map.
INIA 0d4
INIA 0d5
OUTA 0xE3
INIA 0x40
OUTA 0xE4
@@ -350,7 +361,7 @@ putMap:
STA.0
everyRow:
INIA 0d4
INIA 0d5
OUTA 0xE3
SETD.0 MapRow
LDA.0
+43 -4
View File
@@ -5147,7 +5147,13 @@ screenBank:
INIA 0x30
OUTA 0xE2
INIA 0x03
OUTA 0xE8
OUTA 0xE8 ; Four is the atlas: the tiles and the palette.
INIA 0d5
OUTA 0xE3
INIA 0x3A
OUTA 0xE2
INIA 0x03
OUTA 0xE8 ; Five is the screen: the map, or a bitmap.
RET
; Back to the drive whoever called was standing on.
@@ -5178,10 +5184,42 @@ screenPageDirect:
ADD
RET
; ---- And which of the screen's two banks that page is in ----
;
; The tiles and the palette are the atlas and the map is the screen, so the PAGE NUMBER
; ALREADY SAYS WHICH - 0x00 to 0x3F and 0xFC to 0xFF one way, 0x40 to 0xBF the other. Nothing
; has to be remembered alongside the page, and there is no second list to keep in step with
; screenPageFor above.
;
; IN Q AND NOT A, for the same reason screenPageFor answers there: RET puts A back the way it
; found it, so a subroutine that answered in A would answer with the argument it was given.
screenBankFor:
INIB 0x40
CCF
SUB
BRC screenBankAtlas ; Borrowed, so it is below 0x40 and is a tile.
INIB 0xC0
CCF
SUB
BRC screenBankScreen ; Borrowed, so it is below 0xC0 and is the map.
screenBankAtlas:
INIA 0d4
RSTB
CCF
ADD
RET
screenBankScreen:
INIA 0d5
RSTB
CCF
ADD
RET
; A holds a page of video memory. Its 256 bytes come into ScreenBlock.
screenFromVideo:
PSHA
INIA 0d4
CALL screenBankFor ; A is the page and RET hands it back; Q comes back the bank.
PSHA ; The page, which the bank is about to want A for.
MVQA
OUTA 0xE0
POPA
OUTA 0xE1
@@ -5197,8 +5235,9 @@ screenFromVideo:
; A holds a page of video memory. ScreenBlock goes back into it.
screenToVideo:
CALL screenBankFor
PSHA
INIA 0d4
MVQA
OUTA 0xE3
POPA
OUTA 0xE4
+5 -3
View File
@@ -80,8 +80,10 @@ nextBank:
; three bytes into the palette - which means reaching video memory, which means the
; controller.
; Give the screen's memory a bank number. The screen answers on port 0x30, and bank 3 is
; the first number software is allowed to hand out: 0, 1 and 2 belong to the machine.
; Give the screen's memory a bank number. The screen brings TWO banks and this only wants
; one of them: port 0x30 owns the atlas, where the tiles and the palette are, and the
; palette is all this touches. Bank 3 is the first number software is allowed to hand out:
; 0, 1 and 2 belong to the machine.
INIA 0d3
OUTA 0xE3 ; DestBank: the number being given
INIA 0x30
@@ -89,7 +91,7 @@ nextBank:
INIA 0x03
OUTA 0xE8 ; Command: RegisterBank
; The palette sits at the top of video memory, at 0xFC00, and entry n is at n times
; The palette sits at the top of the atlas, 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
+23 -7
View File
@@ -13,13 +13,13 @@
; 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.
; It lives over the top of the map, in the SCREEN bank, because that is the bank whose
; contents change: a picture and a map are the same memory called two different things in two
; different modes. Going to bitmap mode does not clear the text screen, it stops calling it
; one - and coming back finds the map holding whatever the picture put there.
;
; 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.
; WHAT IT DOES NOT COST IS THE FONT. The tiles and the palette are in the other bank and a
; picture cannot reach them, which is what lets this program draw and then say something.
#Program
@@ -27,17 +27,31 @@ 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.
; TWO OF THEM, because the screen brings two banks and this program writes to both: the
; palette is in the atlas and the picture is in the screen.
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
OUTA 0xE8 ; Command: RegisterBank. Three is the atlas.
INIA 0d4
OUTA 0xE3
INIA 0x3A
OUTA 0xE2
INIA 0x03
OUTA 0xE8 ; And four is the screen.
; ---- 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.
; A already holds 3, because RegisterBank is command 3 and the bank wanted is bank 3.
; Leaving the line out would turn a program that says which bank it means into one that
; works by coincidence, and the coincidence breaks the day either number changes.
INIA 0d3 ; splitlint[redundant-assignment]: said rather than coincided
OUTA 0xE3 ; The ATLAS, which registering the second bank moved off
INIA 0xFC
OUTA 0xE4
RSTA
@@ -67,6 +81,8 @@ palette:
; 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.
INIA 0d4
OUTA 0xE3 ; Into the SCREEN bank now, which is where a picture is
RSTA
OUTA 0xE4
OUTA 0xE5 ; Dest 0x0000, the top left corner