
S13 drew a ship into the bitmap and rubbed it out again each frame. Over a plain sky that is a fill. Over scenery it isn't: whatever the ship covered has to come back exactly as it was, every frame, and doing that with the CPU means reading and writing every byte under it.
The command engine copies. One command puts a rectangle of background back from a clean copy kept out of sight; another draws a shape over it with its colour-0 dots left transparent. Together they make a software sprite: as many as you like, as big as you like, in all sixteen colours, over any picture. The TC-01's chip could draw nothing over the screen but its own sprites.
All three take a source (SX, SY), a destination (DX, DY) and a size (NX, NY), in S16's registers, with S16's lines numbered on through the pages.
HMMM (CMR = D0h) copies a rectangle byte for byte. In Graphics 4 a byte is two dots, so it ignores the lowest bit of SX, DX and NX: copies start and end on even dots.
YMMM (CMR = E0h) copies from DX to the right-hand edge of the screen, NY lines from line SY to line DY. There is no SX and no NX: the strip is always as wide as the space from DX to the edge. With DX = 0 that is whole lines, which makes it the simplest way to say "copy these lines there".
LMMM (CMR = 90h + a logical operation) copies dot by dot through S16's logical operations. With TIMP (98h), dots of colour 0 in the source leave the destination as it was: the shape is drawn, its background isn't. With IMP (90h) the 0s are copied too.
HMMM and YMMM move bytes, LMMM moves dots. Use the byte copies where the rectangle starts on an even dot and needs no logical operation, and LMMM where it doesn't.
A copy needs a source that the player never sees. You have three places for one, from S5:
The listing keeps its clean background in page 2 and the ball's picture in the expansion.
Before the command engine can copy a graphic, it has to be in video memory. Put it there the way S5 did: set the address and write the bytes to port 8. Do it once, at the start, into a page you never show. From then on, every copy is the command engine's work, not your program's.
Copies can build a picture as well as move one. Draw one 16 x 16 tile with three HMMV fills. Then copy it to its right: 32 wide. Copy those 32 to their right: 64. Four HMMM copies and the row is 256 dots wide. Then YMMM, from DX = 0, doubles it downwards the same way: 16 lines, 32, 64, 128, 256. Eleven commands fill a whole 32K page.
Three pages of Graphics 4 do the job:
| Page | Lines (y) | Holds |
|---|---|---|
| 1, 08000h | 256-511 | the screen (R#2 = 3Fh) |
| 2, 10000h | 512-767 | a clean copy of the background, never shown |
| expansion | 0- | the ball's picture, 16 x 16 |
Each frame, for each sprite:
With two sprites or more, restore them all, then draw them all. Restore one, draw it, then restore the next, and the second restore can copy clean background over part of the first sprite where they overlap.
Keep x even, because the restore is a byte copy. At an odd x HMMM still copies from the even dot below it, and whenever the ball moves left a column of the old ball's dots is left behind. Either move in steps of two, or restore with LMMM (IMP) instead, which works at any dot.
What it costs: the clean copy is a page of video memory, and a sprite is two commands a frame. The CPU only writes 30 bytes to set them off and polls CE; it never touches the picture's bytes.
; S17: software sprites made by the command engine, in Graphics 4.
; Two balls bounce over a checkerboard. Each frame, for each ball, HMMM
; copies the background back from a clean copy, and LMMM with TIMP
; draws the ball at its new place with colour 0 left transparent.
; Runs for 500 frames (10 seconds).
;
; Where everything is, as the command engine counts lines (256 a page):
; page 1, lines 256-511 (VRAM 8000h, R#2 = 3Fh): the screen
; page 2, lines 512-767 (10000h): the clean background, never shown
; the expansion RAM, at (0,0): the ball's picture
; MOS's own screen, in the first 16K, is left alone.
ORG 256
DI
; --- Graphics 4, showing page 1
LD A, 06h
LD B, 0
CALL setreg
LD A, 3Fh
LD B, 2
CALL setreg
; --- sprites out of the way (attribute table at 1FA00h: Y = 216)
LD A, 7
LD B, 14
CALL setreg
XOR A
OUT (9), A
LD A, 3Ah + 40h
OUT (9), A
LD A, 216
OUT (8), A
LD A, 0F7h
LD B, 5
CALL setreg
LD A, 03h
LD B, 11
CALL setreg
EI
; --- the background, built in page 2 by the command engine
LD HL, tile1 ; a 16x16 tile: colour 4 ...
CALL send
LD HL, tile2 ; ... with two squares of colour 5
CALL send
LD HL, tile3
CALL send
LD HL, 16 ; double it across: 16, 32, 64, 128 -> 256 wide
across: LD (wide+4), HL ; DX = the width so far
LD (wide+8), HL ; NX = the same
PUSH HL
LD HL, wide
CALL send
POP HL
ADD HL, HL
BIT 0, H ; until 256
JR Z, across
LD HL, 16 ; then down with YMMM: 16, 32, 64, 128 -> 256 lines
down: PUSH HL
LD DE, 512
ADD HL, DE
LD (deep+6), HL ; DY = 512 + the depth so far
POP HL
LD (deep+10), HL ; NY = the same
PUSH HL
LD HL, deep
CALL send
POP HL
ADD HL, HL
BIT 0, H
JR Z, down
; --- the screen: the whole background copied to page 1
LD HL, copybg
CALL send
; --- the ball's picture into the expansion RAM
CALL upload
; --- the frame interrupt (S7)
CALL irqon
LD A, 0E0h
LD B, 1
CALL setreg
EI
; --- the game loop: every frame, both balls
main: CALL waitf
LD IX, ball1 ; 1. the background back under both balls
CALL restore
LD IX, ball2
CALL restore
LD IX, ball1 ; 2. move them
CALL move
LD IX, ball2
CALL move
LD IX, ball1 ; 3. draw them at their new places
CALL draw
LD IX, ball2
CALL draw
LD HL, (TICKS)
LD DE, 500
OR A
SBC HL, DE
JR C, main
; --- put the machine back for MOS
LD A, 0C0h
CALL irqoff
CALL tomos
RET
onframe: RET ; nothing to do in the interrupt
; --- restore: HMMM 16x16 from the clean copy, at the ball IX's place
restore:
LD A, (IX+0) ; x
LD (rest+0), A ; SX
LD (rest+4), A ; DX
LD A, (IX+1) ; y
LD (rest+2), A ; SY = 512 + y
LD (rest+6), A ; DY = 256 + y
LD HL, rest
JP send
; --- draw: LMMM with TIMP, the ball's picture to the ball IX's place
draw: LD A, (IX+0)
LD (put+4), A ; DX
LD A, (IX+1)
LD (put+6), A ; DY = 256 + y
LD HL, put
JP send
; --- move: the ball IX steps (IX+2) across and (IX+3) down, bouncing
move: LD A, (IX+0)
ADD A, (IX+2)
LD (IX+0), A
OR A ; x = 0 or 240: turn round
JR Z, mv1
CP 240
JR NZ, mv2
mv1: LD A, (IX+2)
NEG
LD (IX+2), A
mv2: LD A, (IX+1)
ADD A, (IX+3)
LD (IX+1), A
OR A ; y = 0 or 176: turn round
JR Z, mv3
CP 176
RET NZ
mv3: LD A, (IX+3)
NEG
LD (IX+3), A
RET
; x, y, step across, step down - x and every step across even
ball1: DEFB 0, 0, 2, 1
ball2: DEFB 200, 100, -2, -1
; --- upload: the ball's picture into the expansion RAM at (0,0), by the
; CPU: R#45 bit 6 switches the CPU's VRAM access to the expansion.
; 16 lines of 8 bytes, each line 128 bytes on from the last (S11).
upload: DI
LD A, 40h
LD B, 45
CALL setreg ; R#45 = 40h: the CPU reaches the expansion
XOR A
LD B, 14
CALL setreg ; its first 16K
LD HL, 0 ; HL = the line's offset
LD DE, ballpic
LD C, 16 ; 16 lines
up1: LD A, L
OUT (9), A
LD A, H
OR 40h
OUT (9), A ; the line's address, for writing
LD B, 8
up2: LD A, (DE)
OUT (8), A
INC DE
DJNZ up2
PUSH DE
LD DE, 128
ADD HL, DE ; the next line
POP DE
DEC C
JR NZ, up1
XOR A
LD B, 45
CALL setreg ; R#45 = 0: main VRAM again
EI
RET
; --- send: the 15 bytes at HL to R#32-R#46 through port 0Bh, with
; R#17 auto-incrementing; writing R#46 starts the command. Then wait.
send: DI
LD A, 32
LD B, 17
CALL setreg
LD BC, 15*256 + 0Bh
OTIR
EI
cwait: DI ; CE, one read at a time, R#15 back to 0 each time
LD A, 2
LD B, 15
CALL setreg
IN A, (9)
LD C, A
XOR A
LD B, 15
CALL setreg
EI
BIT 0, C
JR NZ, cwait
RET
; Command blocks: SX, SY, DX, DY, NX, NY (words), then CLR, ARG, command.
; SX SY DX DY NX NY
tile1: DEFW 0, 0, 0, 512, 16, 16
DEFB 44h, 0, 0C0h ; HMMV colour 4
tile2: DEFW 0, 0, 8, 512, 8, 8
DEFB 55h, 0, 0C0h ; HMMV colour 5
tile3: DEFW 0, 0, 0, 520, 8, 8
DEFB 55h, 0, 0C0h ; HMMV colour 5
wide: DEFW 0, 512, 0, 512, 0, 16
DEFB 0, 0, 0D0h ; HMMM (DX, NX set above)
deep: DEFW 0, 512, 0, 0, 0, 0
DEFB 0, 0, 0E0h ; YMMM from DX = 0: whole lines
copybg: DEFW 0, 512, 0, 256, 256, 192
DEFB 0, 0, 0D0h ; HMMM: page 2 to page 1
rest: DEFW 0, 512, 0, 256, 16, 16
DEFB 0, 0, 0D0h ; HMMM (x, y set above)
put: DEFW 0, 0, 0, 256, 16, 16
DEFB 0, 10h, 98h ; LMMM, TIMP, source in the expansion (MXS)
; the ball, one row a line, one hex digit a dot: A (colour 10) inside,
; F (15) round the edge, 0 (transparent) outside
ballpic:
DEFB 000h,000h,000h,00Fh,0F0h,000h,000h,000h
DEFB 000h,000h,0FFh,0FFh,0FFh,0FFh,000h,000h
DEFB 000h,00Fh,0FFh,0AAh,0AAh,0FFh,0F0h,000h
DEFB 000h,0FFh,0AAh,0AAh,0AAh,0AAh,0FFh,000h
DEFB 00Fh,0FAh,0AAh,0AAh,0AAh,0AAh,0AFh,0F0h
DEFB 00Fh,0FAh,0AAh,0AAh,0AAh,0AAh,0AFh,0F0h
DEFB 00Fh,0AAh,0AAh,0AAh,0AAh,0AAh,0AAh,0F0h
DEFB 0FFh,0AAh,0AAh,0AAh,0AAh,0AAh,0AAh,0FFh
DEFB 0FFh,0AAh,0AAh,0AAh,0AAh,0AAh,0AAh,0FFh
DEFB 00Fh,0AAh,0AAh,0AAh,0AAh,0AAh,0AAh,0F0h
DEFB 00Fh,0FAh,0AAh,0AAh,0AAh,0AAh,0AFh,0F0h
DEFB 00Fh,0FAh,0AAh,0AAh,0AAh,0AAh,0AFh,0F0h
DEFB 000h,0FFh,0AAh,0AAh,0AAh,0AAh,0FFh,000h
DEFB 000h,00Fh,0FFh,0AAh,0AAh,0FFh,0F0h,000h
DEFB 000h,000h,0FFh,0FFh,0FFh,0FFh,000h,000h
DEFB 000h,000h,000h,00Fh,0F0h,000h,000h,000h
INCLUDE "frame.asm"
Build and run
Save it as s17-copying.asm next to frame.asm. Build and run it as in S2.
What you should see
A checkerboard of blue and light blue 8 x 8 squares filling the screen, and two yellow balls with white rims bouncing round it, two dots across and one line down or up every frame. The squares show through the corners of each ball, and behind them the checkerboard is perfect: no trail, no smudge. After ten seconds, MOS's screen and the prompt.

ball1, change the step across from 2 to 1. What do you expect to see behind that ball, and why only behind that one?put, change ARG from 10h to 00h. Where does LMMM read the ball's picture from now, and what is there?| Symptom | Cause |
|---|---|
| A dotted trail behind the sprite, in its rim's colour | The sprite is at an odd x and the restore is a byte copy (HMMM). Keep x even, or restore with LMMM |
| Small blobs of other shapes where the balls should be | The draw read main video memory instead of the expansion: MXS (10h) missing from ARG |
You can copy rectangles of video memory in bytes (HMMM), in whole lines (YMMM) and in dots through a logical operation (LMMM, with TIMP for transparency), from any page or from the expansion with MXS. A software sprite is a restore from a clean copy and a transparent draw: restore every sprite, then draw every sprite, at even x.
S18, Eighty Columns: the 256's text mode, for menus and status screens.