Einstein 256 Primer cover: Graphics 7's 256 colours on the Einstein 256's screen
← Back to Modules
module
5

192K of Video Memory, and What to Keep in It

Introduction

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.

Eight Pages and a Register

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#14VRAMR#14VRAM
000000h-03FFFh410000h-13FFFh
104000h-07FFFh514000h-17FFFh
208000h-0BFFFh618000h-1BFFFh
30C000h-0FFFFh71C000h-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.

When a Run Crosses a Page

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:

  • In Graphics II (MOS's mode) the address wraps to the start of the same page. A run from 3FFEh puts its third byte at 0000h of that page.
  • In the bitmap modes (Graphics 4 to 7, and Graphics 3) it carries on into the next page: R#14 moves on by one by itself.

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.

The Expansion: 64K More

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.)

What a Game Keeps There

Here is the room you have, against what the screens cost:

ScreenBytesFits in 128K
Graphics II, MOS's layout16K8
Graphics 4, a 256-line page (192 shown)32K4
Graphics 7, a page64K2

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.

What MOS needs left alone

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 Code: Level Maps in Video Memory

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:

Level 1's map of hashes, dots, dollars and an at sign drawn under the MOS prompt

The Code: Screens Drawn in Advance

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:

  • Each screen is two 16K pages. Graphics 4 holds two dots a byte, 128 bytes a line, so lines 0-127 fill one page and lines 128-255 the next. The drawing is done while the mode is still Graphics II, where a run wraps within its page, so fill sets R#14 for each part.
  • The sprites. In the bitmap modes the sprites are in sprite mode 2, and MOS's sprite table would show its text as stray sprites. A one-byte table of the program's own, holding Y = 216 to end the list, lives in the unseen lines under the third screen, at 1FA00h.
  • The wait polls the frame flag in S#0, fifty to a second at the 50 Hz setting from S2. Interrupts stay off throughout, until tomos.
  • The R#2 write lands wherever the picture happens to be. It takes effect from the next line, so a switch made mid-frame shows the top of one screen over the bottom of another for one frame. For a title or a new level that doesn't matter. For animation it does, and S13 makes the switch on the frame interrupt instead.
  • 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.

Screen 1: cyan sky over dark green ground Screen 2: black sky over dark red ground Screen 3: light blue sky over yellow ground

After nine seconds the MOS screen comes back, cleared, with the register display and the prompt.

Change One Thing

  • In s05-level-store.asm, set LEVEL to 0. What should you see?
  • Set 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?
  • In 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?

When It Goes Wrong

SymptomCause
The level draws as a block of solid squaresFetched 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 blankStored in one memory and fetched from the other: R#45 different between the two
No ground: every screen is sky from top to bottomE 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

Summary

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.

Next

S6, Colours of Your Own: the palette port from S4, put to work.

Get the Newsletter

New guides, disk images and community finds, roughly once a quarter. No spam, we promise, this isn't Tatung's marketing department.
Your subscription could not be saved. Please try again.
Your subscription has been successful.

Newsletter

Subscribe to our newsletter and stay updated.