Following on from First Steps in Z80 Assembly Language I'm trying to move a two high character sprite in assembler.
ORG 30000 ; Origin
LASTK EQU 23560 ; last key press (system variable)
PRINT EQU 8252 ; This means the label PRINT equates to 8252.
XOR a ; quick way to load accumulator with zero.
LD A, 2 ; set print channel to screen
CALL 5633 ; Open channel two (ie, write to screen)
LD HL, GFX ; set up UDGs
LD (23675), HL ; where the UDG characters are stored.
CALL 3503 ; clear the screen. CLS
MAINLP CALL PRTPLAY ; print player sprite
HALT ; Slow it down three times
HALT
HALT
LD BC, $FEFE ; load port address into BC, scan for right ("X")
IN A, (C) ; load port data into A
AND %0000100 ; looking for X
JR Z, GORIGHT ; if Z is press, go right
JR MAINLP ; loop back to continue scanning
GORIGHT LD A, (PLAYER+2) ; if player is at right edge, don't continue
CP 31
JR Z, MAINLP ; Jump Relative Zero
CALL UNDRAW
LD A, (PLAYER+2) ; get player's X coordinate
INC A ; add 1
LD (PLAYER+2), A
JR MAINLP
PRTPLAY LD DE, PLAYER ; print player graphic
LD BC, EOPLAYR-PLAYER
CALL PRINT
RET
UNDRAW LD A, " " ; change graphic to empty space
LD (PLAYER+3), A ; store it
CALL PRTPLAY ; undraw graphic from screen
LD A, 144 ; change graphic back to normal
LD (PLAYER+3), A ; store it
RET ; return to basic!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Player x, y
PLAYER DEFB 22, 12, 15, 144 ; print at Y, X, char 144 UDG (A)
DEFB 22, 13, 15, 145 ; print at Y+1, X, char 145 UDG (B)
EOPLAYR EQU $
; Graphics UDG Character
GFX DEFB 6, 62, 124, 52, 62, 60, 24, 60
DEFB 126, 126, 247, 251, 60, 118, 110, 119
The Manic Miner sprite is drawn ok. However when "x" is pressed to go right, only the top half moves. Which either means the undraw isn't working or the bottom character isn't incrementing. I'm very new to assembler and tried to unpick where I had gone wrong. I suspect it's where DEFB are explicitly told to be on 144 & 145, but the undraw is on 144 only. However that should be covered by LD BC, EOPLAYR-PLAYER. Confused.
So you tried to extend the book example by yourself... excellent, this way you will learn most.
Your suspicion is partly correct, the PRTPLAY
does print all bytes.
So it prints first AT control code placing cursor at (x,12), then it prints UDG character 'A' 144, then another AT control code placing cursor at (x,13) and UDG character 'B' 145.
The UNDRAW
does modify only the top-character 144 to space, so it prints space over top of the sprite and prints again UDG 'B' over the bottom, keeping the sprite visible.
To fix this (in the same mundane way how original works):
UNDRAW LD A, " " ; change graphic to empty space
LD (PLAYER+3), A ; replace UDG A 144
LD (PLAYER+7), A ; replace UDG B 145
CALL PRTPLAY ; undraw graphic from screen
LD A, 144 ; change graphic back to normal
LD (PLAYER+3), A ; store it
LD A, 145
LD (PLAYER+7), A
RET ; return to caller
And similarly the routine patching the X coordinate of AT control code has to patch two places now, as there are two AT control codes in your print string:
GORIGHT LD A, (PLAYER+2) ; if player is at right edge, don't continue
CP 31
JR Z, MAINLP ; Jump Relative Zero
CALL UNDRAW
LD A, (PLAYER+2) ; get player's X coordinate
INC A ; add 1
LD (PLAYER+2), A ; update X position of upper char 144
LD (PLAYER+6), A ; update X position of lower char 145
JR MAINLP
You may want to go this kind of tutorial calling ROM using BASIC PRINT
routines for graphics as quickly as possible and look for more advanced tutorials drawing directly into video RAM without ROM, as this is example here is both very slow in terms of performance (would start choking if you would try to animate few more sprites like this, while with optimised assembly you can move like 20+ 8x16 sprites per single frame), and becomes very unwieldy if you want to do larger sprites and suddenly you have to patch many AT control codes and many gfx-characters (144,145), plus it becomes further headache when you need clipping at sprite edges (and you need to print only part of the characters, but not all of them).
I mean you should still try to grasp what the tutorial is doing and how, it's decent tutorial overall and you can exercise your Z80 assembly skill on it, but from the view point of demo-scener doing ZX demos for many years (and aiming for high performance full-screen gfx tricks) - this code is good only to learn how to not write ZX code. :)
Also it may be somewhat easier to follow this tutorial if you are familiar with ZX BASIC and how PRINT
works in BASIC. If you are completely unaware of ZX BASIC and you are just learning Z80 assembly, then this kind of print routines is probably even more confusing then code writing directly into video RAM without ROM routines.