I suppose I am vaguely putting off coding the ZalaXa aliens, so my displacement activity this evening is going to be a scoring routine 🙂
Anyone who has read Jonathan Cauldwell‘s excellent book How To Write ZX Spectrum Games will be nodding sagely during the next part! If you haven’t, I highly recommend doing so. Jon is a highly respected legend who kept the homebrew scene alive in the darkest days, and is still writing games and contributing behind the scenes to many aspects of the scene today. The book is available in a 97 page PDF on his website, and in bitesize chunks deep in the chuntey field. The scoring chapter I’m referring to is Chapter 10—it wouldn’t go amiss to take a small detour and read it before continuing.
In the code for part 10, I’ve added a variant of Jonathan’s routine. The first part (I hope) is more or less the same, except he likes to store his numbers as ASCII codes (48 to 57), whereas I prefer to store them as indices (0 to 9) because indices are more useful when working with table offsets.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
; utilities.asm UpdateScore proc ld a, (hl) ; Current value of digit add a, b ; Add points to this digit ld (hl), a ; Place new digit back in score cp 10 ; More than nine? jp c, Redraw ; No - relax. sub 10 ; Subtract 10 ld (hl), a ; Put new digit back in score Digit: dec hl ; Previous digit in score inc (hl) ; Up this by one ld a, (hl) ; What's the new value? cp 10 ; Gone past nine? jp c, Redraw ; No, scoring done sub 10 ; Down by ten ld (hl), a ; Put it back jp Digit ; Go round again Redraw:
What this code does (and feel free to trace through it with with the Zeus debugger), is take the address of the digit (0 to 5) you pass in
hl, and add the number you pass in
b to it (1 to 9), carrying over into the left digits as necessary.
We call it like this, from the
AnimateDemo routine that checks for the
M key and flashes the border red:
5 6 7 8 9 10 11 12 13 14 15 16
; sprites.asm ld bc, zeuskeyaddr("M") ; Get the I/O port address for M (fire) in a, (c) ; Read keys and zeuskeymask("M") ; Mask out everything but M ld a, Black ; Assume black border jp nz, SetBorder ; if M was not pressed. ld hl, Score.D1 ; Otherwise ld b, 1 ; add 10 to call UpdateScore ; score, ld a, Red ; then assume red border. SetBorder:
The key parts here are lines 12 to 14, which adds 1 to the the second digit1—in other words, it adds 10 to the score.
The second half of
UpdateScore involves drawing the score on the screen, and looks like this:
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
; utilities.asm Redraw: ld de, $1000 ; $1000 is 0, 16 in NIRVANA+ coordinates ld b, 6 ; Process 6 digits, ld hl, Score.D5 ; starting with the leftmost digit. RedrawLoop: ld a, (hl) ; Read value of score digit (0..9, not ASCII) or a ; Fast way to compare with zero SkipSMC equ $+1: jr z, SkipDraw ; <SMC If leading zero, skip this digit push hl ; Otherwise, save digit address call NIRVANA_printC ; Print character (a=index, d=pxline, e=column) pop hl ; Restore digit address inc e ; Increase column xor a ; Set the skip to never skip any zeroes ld (SkipSMC), a ; SMC> by changing it to jp +0 SkipDraw: inc hl ; Set next digit for processing djnz RedrawLoop ; Reduce the digit count and repeat until zero ld a, $0A ; Set the skip to skip leading zeroes again ld (SkipSMC), a ; SMC> by changing it to jp +$0A ret pend
The main thrust of this is that it calls one of the NIRVANA+ API functions,
NIRVANA_printC. This takes a character index in
a, a pixel line Y coordinate in
d, and a column coordinate (0 to 31) in
There’s a fancy piece of self-modified code that drops the leading zeroes, but retains any trailing zeroes. This is certainly not the only way to do that, but I went with this method in the moment.
The character indices refer to a NIRVANA+ table called
CHAR_TABLE, which contains character data in sets of 8 bytes—the same format as the Spectrum user-defined graphics.
It’s worth mentioning that I used
NIRVANA_printC instead of the FZX print routine, mostly because
NIRVANA_printC is about as optimised as it gets—even though that means storing two copies of the digit graphics in memory.
I shifted these characters down one pixel from how they appear in the Namco.fzx font. I hadn’t anticipated this, but NIRVANA+ only allows you to render characters and tiles on even-numbered pixel lines, and in part 9 I had chosen to print the white scores on an odd-numbered line to give a two pixel gap between them and the red labels above.
The character data looks like this:
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
; database.asm ; ASM data file from a ZX-Paintbrush picture with 8 x 8 pixels (= 1 x 1 characters) Char proc Table: ::CHAR_TABLE: No equ 0 ; R0 R1 R2 R3 R4 R5 R6 R7 CharID Notes db $00, $38, $4C, $C6, $C6, $C6, $64, $38 ; 00 Score digit 0 db $00, $18, $38, $18, $18, $18, $18, $7E ; 01 Score digit 1 db $00, $7C, $C6, $0E, $3C, $78, $E0, $FE ; 02 Score digit 2 db $00, $7E, $0C, $18, $3C, $06, $C6, $7C ; 03 Score digit 3 db $00, $1C, $3C, $6C, $CC, $FE, $0C, $0C ; 04 Score digit 4 db $00, $FC, $C0, $FC, $06, $06, $C6, $7C ; 05 Score digit 5 db $00, $3C, $60, $C0, $FC, $C6, $C6, $7C ; 06 Score digit 6 db $00, $FE, $C6, $0C, $18, $30, $30, $30 ; 07 Score digit 7 db $00, $78, $C4, $E4, $78, $9E, $86, $7C ; 08 Score digit 8 db $00, $7C, $C6, $C6, $7E, $06, $0C, $78 ; 09 Score digit 9 Count equ ($-Table)/8 if Count > 255 zeuserror "Character table has grown larger than 255 entries." endif pend
There’s a fancy way we can generate this data in
db format from a
.scr file using ZX-Paintbrush. I will talk about this in a future blog post.
The end result, when we hit the
M key, is that the score counts up—quite fast, in full left-justified glory, and in conjunction with the border flashing red. We’ll certainly need to put some limit on the rate of fire when we come to program the bullet routines, but it’s fine for now.
I rather suspect that this routine is not optimised enough for the final game, and we will have to revisit it later—perhaps by printing a single score digit in six successive frames, or some other trickery. We will see!