
On the TC-01 there was 16K of video memory and the screen took nearly all of it. Tiles, font, sprites, the name table: every byte had a job, and your level data had to live in the 64K of main RAM along with your code. The 256 has twelve times as much video memory, 192K, beside the same 64K for the CPU. Most of it is never on screen. This section shows how to reach it, and what a game keeps there.
The ports work as they always did: the low byte of an address, then the high six bits (with 40h to write) to port 9, then data through port 8. Those fourteen bits reach 16K, and on the TC-01 that was all there was.
On the 256, register 14 supplies three more address bits, 16 to 14. It picks one of eight 16K pages, and the fourteen bits you send through port 9 reach a byte within it:
| R#14 | VRAM | R#14 | VRAM |
|---|---|---|---|
| 0 | 00000h-03FFFh | 4 | 10000h-13FFFh |
| 1 | 04000h-07FFFh | 5 | 14000h-17FFFh |
| 2 | 08000h-0BFFFh | 6 | 18000h-1BFFFh |
| 3 | 0C000h-0FFFFh | 7 | 1C000h-1FFFFh |
Write R#14 first, then the address, then the data:
DI
LD A, 4
OUT (9), A
LD A, 80h+14
OUT (9), A ; R#14 = 4: the 16K at 10000h
XOR A
OUT (9), A ; address low byte 00h
LD A, 40h
OUT (9), A ; high six bits 0, plus 40h: write at 10000h
... ; bytes to port 8
Two things to remember about R#14. You can't read it back, so if your program needs to know which page it last chose, keep a copy in RAM. And MOS expects it at 0: set it back before you print or return.
MOS has a call for it too. D8 takes the page in A, as S4 listed. Writing the register yourself costs two OUTs and no trip through MOS.
At start-up MOS uses only page 0, exactly as on the TC-01, and every byte from 04000h to 1FFFFh is 0. That is 112K your program can have to itself while MOS's screen stays as it is.
Write a run of bytes through port 8 and the address moves on after each one. What happens at the end of a 16K page depends on the mode:
So in the bitmap modes a 32K screen can be written in one run, and a whole picture is no harder than a line. In Graphics II, or while the screen is still MOS's, set R#14 again at each 16K boundary. The program at the end of this section draws its screens while MOS's mode is showing, and does exactly that.
Behind the 128K there is a further 64K, the expansion memory. Set bit 6 of register 45 and the CPU's reads and writes go there instead of to main video memory. It has four 16K pages, so R#14 = 0 to 3 reach it. Pages 4 to 7 don't exist there: reading them gives FFh.
LD A, 40h
OUT (9), A
LD A, 80h+45
OUT (9), A ; R#45 bit 6: the CPU now reaches the expansion
... ; R#14 = 0-3, the address, the data, as before
XOR A
OUT (9), A
LD A, 80h+45
OUT (9), A ; R#45 = 0: main video memory again
The expansion is never on screen. It is a store. The CPU can reach it as above, and so can the command engine: bit 5 of R#45 (MXD) makes a command write into the expansion, and bit 4 (MXS) makes it read its source from there. So graphics kept in the expansion can be copied onto the screen in one command, which S17 does.
Clear bit 6 before you hand the screen back. With it left set, MOS's printing stops appearing. (MOS's BE call clears it, but it's tidier never to leave it set.)
Here is the room you have, against what the screens cost:
| Screen | Bytes | Fits in 128K |
|---|---|---|
| Graphics II, MOS's layout | 16K | 8 |
| Graphics 4, a 256-line page (192 shown) | 32K | 4 |
| Graphics 7, a page | 64K | 2 |
Most games need only one of those on screen. The rest is yours, and there are four ways to use it.
Screens drawn in advance. In the bitmap modes, register 2 chooses which page is shown. In Graphics 4, R#2 = 1Fh shows the page at 00000h, 3Fh the one at 08000h, 5Fh 10000h and 7Fh 18000h. In Graphics 7, 1Fh and 3Fh show 00000h and 10000h. What the CPU writes (R#14) and what the screen shows (R#2) are separate, so you can draw a title screen, a high-score table and the first level while another is showing, and then change one register to show any of them complete. No drawing time, no half-drawn screen. S13 takes this further, drawing every frame on the hidden page and swapping on the frame interrupt for flicker-free animation.
Graphics kept off screen. A Graphics 4 page is 256 lines tall but shows 192 (212 with R#9 bit 7). The lines below are VRAM nobody sees: 8K under each page, a natural place for a sprite table, a set of tiles or a software sprite's images. Whole pages can hold nothing but graphics. The command engine can reach every page, so it can copy from them onto the screen: S17 does that.
Level data in VRAM. Level maps, enemy tables and text take main RAM that your code would like. Kept in video memory they cost none. A 32 x 24 map of tile numbers is 768 bytes: a 16K page holds 21 of them, the expansion 85. At the start of a level, read the map back into RAM, or read just the row you need. Reads need no delay, so a plain counted loop of IN A,(8) fetches them as fast as you can store them.
Tables moved out of the way. In Graphics II every table can live above 16K: R#10 and R#11 and the high bits of R#2, R#4 and R#6 point the chip at them. In sprite mode 2 (S8) the sprite tables can go anywhere in the 128K. So even in the familiar mode you can lift the whole screen out of page 0 or keep several sets of tables and switch between them.
MOS's Graphics II screen lives in 00000h-03FFFh, and the way back to MOS, tomos (S4), relies on it being intact. Keep your own pages and data elsewhere and returning to MOS is clean. If you set up MOS's Graphics 6 screen with SETMOD, MOS uses far more: the bitmap at 0000h-BFFFh and its tables above that, up to FA80h.
The first program stores two level maps in page 4, as a game would at start-up, then fetches one back into RAM and draws it on MOS's screen. Everything happens in MOS's own mode, so no tomos is needed: just R#14 and R#45 back to 0 before printing.
; S5: keep a game's level maps in video memory, and fetch one when needed.
; Two level maps (6 rows of 20 characters) are written into VRAM page 4,
; at 10000h and 10080h, well clear of MOS's screen in page 0. Then the
; level chosen by LEVEL is read back into a buffer and drawn on MOS's
; screen, a character at a time.
LEVEL: EQU 1 ; which level to draw: 0 or 1
PAGE: EQU 4 ; the 16K page the maps live in (R#14)
MXC: EQU 00h ; R#45: 00h main VRAM, 40h the 64K expansion
ROWS: EQU 6
COLS: EQU 20
SIZE: EQU ROWS*COLS ; 120 bytes a level
SLOT: EQU 128 ; each level starts 128 bytes after the last
ORG 256
; --- store both levels in VRAM, as a game would at start-up
DI
CALL vpage ; R#45 and R#14: our page
XOR A ; VRAM address 0000h of the page, for writing
OUT (9), A
LD A, 40h ; high six bits 0, plus 40h: write
OUT (9), A
LD HL, maps
LD BC, 2*SLOT ; both slots, one run of writes
st1: LD A, (HL)
OUT (8), A
INC HL
DEC BC
LD A, B
OR C
JR NZ, st1
CALL vhome ; R#14 and R#45 back to 0 for MOS
EI
; --- later: fetch one level back into a buffer in main RAM
DI
CALL vpage
LD A, LEVEL*SLOT ; low byte of the level's offset
OUT (9), A
XOR A ; high six bits 0, no 40h: read
OUT (9), A
LD HL, buf
LD BC, SIZE
ld1: IN A, (8) ; each read moves the VRAM address on by one
LD (HL), A
INC HL
DEC BC
LD A, B
OR C
JR NZ, ld1
CALL vhome
EI
; --- draw it: ROWS lines of COLS characters
LD HL, buf
LD D, ROWS
row: LD A, 13
RST 8
DEFB 158
LD A, 10
RST 8
DEFB 158 ; a new line
LD E, COLS
col: LD A, (HL)
RST 8
DEFB 158 ; call 158 leaves HL alone
INC HL
DEC E
JR NZ, col
DEC D
JR NZ, row
RET
; --- vpage: point the CPU at our store (R#45 = MXC, R#14 = PAGE)
vpage: LD A, MXC
LD B, 45
CALL setreg
LD A, PAGE
LD B, 14
JR setreg
; --- vhome: R#14 = 0 and R#45 = 0, as MOS expects them
vhome: XOR A
LD B, 14
CALL setreg
XOR A
LD B, 45
; fall into setreg
; --- setreg: write A to VDP register B
setreg: OUT (9), A
LD A, B
OR 128
OUT (9), A
RET
; --- the two levels, each padded to 128 bytes
maps: DEFB '####################'
DEFB '#..$.....#.......$.#'
DEFB '#..####..#..####...#'
DEFB '#.....#.....#..@...#'
DEFB '#$....#..$..#......#'
DEFB '####################'
DEFS SLOT-SIZE
DEFB '####################'
DEFB '#........$.........#'
DEFB '#.######.###.#####.#'
DEFB '#.#$...#...#.....#.#'
DEFB '#...@..#.$.....$...#'
DEFB '####################'
DEFS SLOT-SIZE
buf: DEFS SIZE ; where a level is fetched to
Interrupts are off whenever the program is in the middle of setting a VRAM address or moving data, so nothing else can touch the chip between the address and the bytes. The buffer starts empty: anything drawn came back out of video memory. A real game would draw tiles from the map rather than characters, but the fetch is the same.
Build and run
Save it as s05-level-store.asm. Build and run it as in S2.
What you should see
Level 1, under the > prompt, then the register display:

The second program draws three Graphics 4 screens, a sky and a ground in different colours for three "levels", into 08000h, 10000h and 18000h. It does this while MOS's screen is still showing. Then it switches to Graphics 4 and shows each screen for a second, three times round, by writing R#2 and nothing else, and finally puts MOS's screen back with tomos.
; S5: three screens drawn in advance, then shown in turn by R#2 alone.
; While MOS's own screen is still showing, three Graphics 4 screens are
; drawn into the VRAM above it, at 08000h, 10000h and 18000h: a sky and a
; ground, in different colours for each "level". Then the program switches
; to Graphics 4 and shows each screen for one second, three times round,
; by writing R#2 and nothing else. Then it puts MOS's screen back.
ORG 256
DI
; --- draw the three screens (the display is still MOS's)
LD HL, levels
LD D, 2 ; screen 1 is 16K pages 2 and 3 (08000h-0FFFFh)
draw: LD E, (HL) ; the sky's byte: two pixels of one colour
INC HL
LD C, (HL) ; the ground's byte
INC HL
PUSH HL
LD HL, 0 ; lines 0-127: the whole first 16K, sky
LD B, 128 ; 128 lines of 128 bytes
CALL fill
INC D ; the second 16K: lines 128-191
LD HL, 0
LD B, 32 ; lines 128-159, sky
CALL fill
LD E, C
LD HL, 32*128 ; from line 160 ...
LD B, 32 ; ... to 191, ground
CALL fill
INC D ; the next screen starts on the next page
POP HL
LD A, D
CP 8
JR NZ, draw
; --- no sprites: our own attribute table, holding only Y = 216
LD D, 7
LD HL, 3A00h ; 1FA00h: below screen 3's 192 lines
CALL setw
LD A, 216
OUT (8), A
LD A, 0F7h
LD B, 5
CALL setreg ; R#5 = F7h ...
LD A, 03h
LD B, 11
CALL setreg ; ... and R#11 = 03h: attributes at 1FA00h
; --- Graphics 4, then each screen in turn for 50 frames
LD A, 06h
LD B, 0
CALL setreg ; R#0 = 06h: Graphics 4
LD C, 3 ; three times round
round: LD HL, shows
LD E, 3 ; three screens
next: LD A, (HL) ; this screen's R#2 value
INC HL
LD B, 2
CALL setreg ; R#2: show it - the only write
LD D, 50
wait: IN A, (9) ; S#0 (R#15 is 0): bit 7 is the frame flag
RLCA
JR NC, wait
DEC D
JR NZ, wait ; one second
DEC E
JR NZ, next
DEC C
JR NZ, round
CALL tomos ; MOS's screen back (and EI)
RET
; --- fill: B lines of 128 bytes of E, from offset HL of 16K page D
; (C, D and E come back unchanged)
fill: CALL setw
fl1: PUSH BC
LD B, 128
fl2: LD A, E
OUT (8), A
DJNZ fl2
POP BC
DJNZ fl1
RET
; --- setw: R#14 = D, then the write address HL (0000h-3FFFh) in that page
setw: PUSH BC
LD A, D
LD B, 14
CALL setreg
LD A, L
OUT (9), A
LD A, H
OR 40h ; 40h: for writing
OUT (9), A
POP BC
RET
; sky and ground for each screen: colour n twice in a byte is n*11h
levels: DEFB 77h, 0CCh ; cyan sky, dark green ground
DEFB 11h, 66h ; black sky, dark red ground
DEFB 55h, 0AAh ; light blue sky, yellow ground
; R#2 for each screen: 3Fh 08000h, 5Fh 10000h, 7Fh 18000h
shows: DEFB 3Fh, 5Fh, 7Fh
onframe: RET ; frame.asm wants one; this program uses no interrupt
INCLUDE "frame.asm"
A few things to notice:
fill sets R#14 for each part.tomos.frame.asm is included for tomos. Its other routines aren't used here.Build and run
Save it as s05-screens.asm in the same folder as frame.asm. Build and run it as in S2.
What you should see
Each of the three screens in turn, a second each, three times round. Each is complete from the moment it appears: 160 lines of sky over 32 of ground, edge to edge.

After nine seconds the MOS screen comes back, cleared, with the register display and the prompt.
s05-level-store.asm, set LEVEL to 0. What should you see?MXC to 40h, so the maps go into the expansion, and leave PAGE at 4. What will the fetch bring back, and why? Then set PAGE to 1. What now?s05-screens.asm, change the second entry of shows from 5Fh to 1Fh. Which page does the screen show for that second, and what is in it?| Symptom | Cause |
|---|---|
| The level draws as a block of solid squares | Fetched from the expansion with R#14 at 4 or more: those pages don't exist there and read FFh, which prints as a solid character |
| The level is blank | Stored in one memory and fetched from the other: R#45 different between the two |
| No ground: every screen is sky from top to bottom | E still held the sky's byte when fill drew the ground. fill uses whatever is in E, so LD E, C must come before that call |
You can reach all 192K: R#14 picks the 16K page, R#45 bit 6 switches to the expansion's four pages, and runs carry across pages in the bitmap modes but wrap in Graphics II. You know what fits where, and four things a game keeps there: screens shown by R#2 in an instant, graphics off screen, level data fetched when needed, and tables moved out of MOS's way. Always leave R#14 and R#45 at 0 for MOS.
S6, Colours of Your Own: the palette port from S4, put to work.