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

The 256's Video Chip

Introduction

In the assembly course the video chip was a box with two doors (S15): port 8 for data, port 9 for addresses, registers and, from S24 on, status. Eight registers, one status byte, 16K behind it. The 256's chip keeps both doors exactly where they were and works through them the same way. Everything you wrote for the TC-01's screen still reaches it. But there are two more doors, far more registers behind them, and ten status registers where there was one. This section is the map. Later sections take each part in turn and put it to work in a game.

What Stays the Same

Ports 8 and 9 behave as you know them. A register is written as its value, then 80h plus its number, to port 9. A VRAM address is the low byte, then the high six bits (plus 40h to write) to port 9, and the data moves through port 8, the address moving on by one each time. Reads work the same way, and a read-modify-write needs no delay. At start-up MOS 2.1 lays out the first 16K exactly as MOS 1.2 did, so the assembly course's tables, font and sprite attributes are where you left them.

What's different is that this 16K is only the start. Register 14 picks which 16K of a much bigger memory those fourteen address bits land in. That is S5's subject.

Two New Ports

PortWhat it does
0AhThe palette. Write a colour number to register 16, then two bytes here: 0RRR0BBB, then 00000GGG, each level 0-7. Keep sending pairs and they fill the next colour, and the next. S6 covers it.
0BhIndirect register writes. Write a register number to register 17, then send values here. Each one lands in a register without the 80h+n byte.

Port 0Bh is the one to understand now, because MOS's calls and the command engine (S16) lean on it. Register 17 holds a register number and one switch:

R#17 bitMeaning
7 (AII)0: after each byte to port 0Bh, move on to the next register. 1: stay on the same register.
5-0The register the next byte goes to.

With bit 7 clear, one register number and a run of bytes fill a block of consecutive registers. Set R#17 to 6 and send two bytes to port 0Bh, and the first goes to register 6, the second to register 7. It suits registers that sit side by side and are set together, and MOS's call D6 (below) works through the same port. With bit 7 set, every byte goes to the same register, which suits a register you write over and over, such as the scroll register.

Register 17 itself is always written the ordinary way, through port 9.

Forty-Seven Registers

The TC-01's chip had registers 0 to 7. The 256's has 0 to 46. You met the consequence in S3: a register number above 7 now reaches a real register. Here are the ones the primer uses, and the sections that teach them.

RegisterWhat it doesSection
R#0The mode, as before, with new values: 02h Graphics II (MOS's), 04h Graphics 3, 06h Graphics 4, 08h Graphics 5, 0Ah Graphics 6, 0Eh Graphics 7. Bit 4 turns on the line interruptS8, S11, S12, S15
R#1As before. Bit 5 now really does start the frame interrupt (S3). Bit 4 with R#0 = 04h gives 80 columnsS7, S18
R#2The name table in Graphics II; in the bitmap modes, which page is shownS5, S13
R#5, R#11The sprite attribute table, anywhere in memoryS8
R#6The sprite patterns, anywhere in memoryS8
R#7The backdrop, as before
R#8Bit 5 (TP): colour 0 becomes solid. Bit 1 (SPD): every sprite hiddenS6, S9
R#9Bit 7: 212 lines instead of 192. Bits 3 and 2: interlace. The value 2 forces 50 HzS3, S11, S12
R#10With R#11 and the top bits of R#2, R#4 and R#6, moves any Graphics II table above 16KS5
R#14Which 16K of video memory the CPU reachesS5
R#15Which status register port 9 readsbelow
R#16Which colour port 0Ah writesS6
R#17Which register port 0Bh writesabove
R#18Nudges the whole picture a dot or a line in any directionS14
R#19The picture line that raises the line flagS15
R#23Vertical scroll, one line per stepS14
R#32-R#46The command engine: coordinates, sizes, colour, argument and command. Writing R#46 starts a command. R#45 also switches memory: bit 6 points the CPU at the 64K expansion, bits 5 and 4 the command engineS5, S16, S17

Ten Status Registers

On the TC-01 port 9 gave you one status byte. On the 256 there are ten, S#0 to S#9, and register 15 chooses which one port 9 returns. Write n to R#15, read port 9, and you have S#n. With R#15 at 0, a plain IN A,(9) reads S#0, the one you know.

StatusBits the primer uses
S#0Bit 7 the frame flag and bit 5 sprite coincidence, as before. In sprite mode 2, bit 6 says nine sprites met on a line and bits 4-0 say which (S8)
S#1Bit 0 the line flag (S15). Bits 5-1 the chip's ID number: 0, a V9938
S#2Bit 0 (CE): the command engine is busy (S16). Bit 6 rises once a frame. Bits 3 and 2 always read 1
S#7The colour a POINT command read (S16)

S#2's two fixed bits make a handy check that R#15 really did change: a byte from S#2 always has bits 3 and 2 set.

Always back to 0

Reading another status register is three steps, and between them R#15 is not 0. Anything that reads port 9 expecting S#0 in that gap, an interrupt handler above all, gets the wrong register. Once your program has a frame interrupt (S7) this is not an inconvenience but a fault: a handler that reads port 9 to clear the frame flag reads another status register instead, the interrupt is never cleared, and the handler runs again and again. So every status read follows one pattern:

        DI
        LD A, 2         ; the status register you want
        OUT (9), A
        LD A, 80h+15
        OUT (9), A      ; R#15 = 2
        IN A, (9)       ; S#2
        LD C, A         ; keep it
        XOR A
        OUT (9), A
        LD A, 80h+15
        OUT (9), A      ; R#15 = 0: S#0 again
        EI

Interrupts off, one read, R#15 back to 0, interrupts on. When you wait for the command engine in S16 you will poll S#2 like this, one read at a time, rather than holding interrupts off for the whole wait.

The New MOS Calls

MOS 2.1 has calls of its own for the new chip. You reach them with RST 8 and DEFB as always.

CallWhat it does
D5Writes register C with the value in B
D6Copies bytes from (HL) into registers B to C, one after another, through port 0Bh
D7Reads status register A into A. It leaves R#15 at that number: set it back to 0
D8Points the CPU at the 16K page in A: 0-7 in main video memory, and with bit 3 set (8-11) the expansion's pages 0-3
DASets VRAM address bits 15 and 14 from the top two bits of B
C1-C3Set a 14-bit VRAM address within the page D8 last chose, without touching R#14
DBSets register 7, the backdrop, from the colours MOS keeps at FB36h and FB38h
D9Sends the fifteen bytes at FC0Dh-FC1Bh to R#32-R#46: one command-engine command
DCCopies MOS's COPY (A = 0) or BACKUP (A = 1) utility from ROM to 0100h
92SETMOD: HL = 0 to 6 sets and clears a screen: Graphics II at 32 or 40 columns, 80-column text, or Graphics 6 at 32, 40, 64 or 80 columns. HL above 6 returns with carry set and changes nothing. The MOS command S0 to S6 does the same

Three cautions. D6 reads its table with the ROM paged over the bottom 32K, as S3 warned, so keep the table above 8000h. DC writes over 0100h, which is where your program is, so only a program running above 8000h can use it. And SETMOD into Graphics 6 leaves R#15 at 2: put it back to 0 before you read S#0.

D5 is the easy way to try a register from a program, at the cost of a call through MOS each time. In a game's inner loop you write the ports yourself.

Leaving the Machine Tidy

On the TC-01 a program could change the screen and return, and MOS carried on. On the 256 there is far more state to leave behind, and returning to MOS repairs none of it. A program that changed the mode, the palette, the scroll, R#9, R#18 or R#45 returns to a MOS screen still showing those changes. R#45 is the worst: with bit 6 left set, MOS's printing stops appearing at all.

MCAL BE, which clears to a 40-column screen on the TC-01, does much more on the 256: it puts back MOS's Graphics II screen, the font, the sprites, the sound chip and the start-up palette, and sets R#14, R#15 and R#23 to 0. What it does not put back is a mode you set by writing R#0 yourself. So a program that leaves MOS's mode does this on the way out:

  1. If it installed its own interrupt table (S7): interrupts off, R#1 with bit 5 clear, I back to FBh.
  2. R#0 = 02h, R#2 = 0Eh, R#5 = 76h, R#11 = 0, R#14 = 0: MOS's mode and tables.
  3. RST 8 / DEFB 0BEh.

The primer's listings share a short file, frame.asm, that S7 explains line by line. Its irqoff routine is step 1 and its tomos routine is steps 2 and 3. From S5 on, a listing that changes mode ends with CALL tomos, or with the same register writes from a table of its own, then BE.

The Code

A program that uses both new mechanisms. It reads S#1 and S#2 through R#15, prints the chip's ID and S#2's fixed bits, and then writes registers 6 and 7 with one register number and two bytes to port 0Bh. Register 6 gets MOS's own value back (03h: sprite patterns at 1800h), and register 7 gets F6h, the backdrop in colour 6.

; S4: the 256's new video ports at work.
; 1. Read status registers S#1 and S#2 through R#15, and print the chip's
;    ID number (S#1 bits 5-1) and S#2's two fixed bits (bits 3-2).
; 2. Write registers 6 and 7 in one go through port 0Bh, with R#17's
;    auto-increment: R#7 = F6h turns the backdrop dark red.

        ORG 256

        ; --- 1. read two status registers, under DI
        DI
        LD A, 1
        LD B, 15
        CALL setreg     ; R#15 = 1: port 9 now reads S#1
        IN A, (9)
        LD (s1), A
        LD A, 2
        LD B, 15
        CALL setreg     ; R#15 = 2: S#2
        IN A, (9)
        LD (s2), A
        XOR A
        LD B, 15
        CALL setreg     ; R#15 = 0: always put S#0 back
        EI

        LD HL, msg1
        CALL puts       ; "CHIP ID "
        LD A, (s1)
        RRCA            ; bits 5-1 down to 4-0
        AND 1Fh
        CALL phex
        LD HL, msg2
        CALL puts       ; "  S#2 BITS 3-2 "
        LD A, (s2)
        AND 0Ch         ; keep bits 3 and 2
        CALL phex

        ; --- 2. an indirect write, auto-incrementing
        DI
        LD A, 6         ; bit 7 (AII) clear: auto-increment on
        LD B, 17
        CALL setreg     ; R#17 = 6: the next byte to port 0Bh goes to R#6
        LD A, 03h
        OUT (0Bh), A    ; R#6 = 03h (sprite patterns at 1800h, as MOS has it)
        LD A, 0F6h
        OUT (0Bh), A    ; ... and this one to R#7: backdrop colour 6
        EI
        RET

; --- setreg: write A to VDP register B (the TC-01 way, through port 9)
setreg: OUT (9), A
        LD A, B
        OR 128
        OUT (9), A
        RET

; --- puts: print the string at HL, ending in 0
puts:   LD A, (HL)
        OR A
        RET Z
        RST 8
        DEFB 158
        INC HL
        JR puts

; --- phex: print A as two hex digits
phex:   PUSH AF
        RRCA
        RRCA
        RRCA
        RRCA
        CALL pnib       ; the high digit
        POP AF          ; then the low
pnib:   AND 0Fh
        ADD A, '0'
        CP '9'+1
        JR C, pn1
        ADD A, 'A'-'9'-1
pn1:    RST 8
        DEFB 158
        RET

msg1:   DEFB 'CHIP ID ', 0
msg2:   DEFB '  S#2 BITS 3-2 ', 0
s1:     DEFB 0
s2:     DEFB 0

puts relies on call 158 leaving HL alone, as it did on the TC-01. This program leaves MOS's mode alone, so it needs no tomos; the dark red backdrop it leaves behind is the result you are looking for.

Build and run

Save it as s04-new-ports.asm. Build and run it as in S2, with the extension's Run, as in the assembly course.

What you should see

>CHIP ID 00  S#2 BITS 3-2 0C

then the register display and the prompt, and the whole background turns dark red, colour 6, as it did when you wrote register 7 in the assembly course. The text stays white.

The start-up screen on a dark red backdrop, with CHIP ID 00 and S#2 BITS 3-2 0C printed under Ready

00 is the ID of the chip, and 0C proves the second read came from S#2: bits 3 and 2, always set there. The red proves two bytes to port 0Bh reached two different registers. Had both gone to register 6, the backdrop would still be blue.

Change One Thing

  • Change the 6 loaded for R#17 to 86h, setting the AII bit. Where do the two bytes go now, and what colour is the backdrop?
  • Replace the whole of part 2, from the second DI to the EI, with LD B, 0F6h, LD C, 7, RST 8, DEFB 0D5h. What should you see, and what has the program given up by letting MOS do it?
  • Read S#2 a dozen times in a row and print each one without the AND 0Ch. Which bits stay the same, and which might not?

When It Goes Wrong

SymptomCause
The ID prints as 02 and bits 3-2 as 04R#15 was not written, or written with 80h + 15 missing, so port 9 returned S#0 both times
The backdrop stays blueR#17's bit 7 was set, so both bytes went to register 6

Summary

You know the 256's video chip around the edges: port 0Ah for the palette, port 0Bh with R#17 for writing registers in runs, registers up to 46 and what each section does with them, and ten status registers read through R#15, always under DI and always put back to 0. You know MOS's new calls, and what they cost. And you know the exit routine: R#0 and the tables back by hand, then BE.

Next

S5, 192K of Video Memory, and What to Keep in It: register 14 and the memory that lies beyond the TC-01's 16K, and how a game fills it.

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.