ZX Spectrum Assembly, Space Battle – 0x02 Painting UDG
In this chapter of ZX Spectrum Assembly, we will start drawing with UDG.
Translation by Felipe Monge Corbalán
Table of contents
- Painting UDG
- Where are the UDG?
- We paint our UDGs
- We load the enemies’ UDGs
- ZX Spectrum Assembly, Space Battle
- Useful links
Painting UDG
The ZX Spectrum character map consists of two hundred and fifty-six values, of which we can redefine twenty-one, namely those between $90 (144) and $A4 (164) inclusive.
Create a folder called Step02 and copy var.asm from Step01.
Where are the UDG?
The value of memory address $5C7B contains the memory address where the user-defined graphics are located, so all we need to do is load the address where our graphics are defined into that memory address. Once this is done, drawing any character between $90 and $A4 with RST $10 will draw the graphics we have defined.
We create the file called const.asm and add the following:
; Memory address where user-defined graphics are loaded.
UDG: EQU $5c7b
In this constant we will have the address where we store the address where our graphics are located.
We paint our UDGs
It’s time for our first test, we’re going to paint the UDGs. Create main.asm and add the following lines:
org $5dad
Main:
ld a, $90
ld b, $15
Loop:
push af
rst $10
pop af
inc a
djnz Loop
ret
end Main
The first thing to do is to specify the address where we want to load the program, ORG $5DAD. We load the program at position $5DAD (23981) as it will be compatible with 16K models.
The next line is a label, Main, the entry point of the program.
Next we load one hundred and forty-four into A, LD A, $90, and twenty-one into B, LD B, $15, so we paint from character one hundred and forty-four to one hundred and sixty-four, for a total of twenty-one characters.
We make a loop of twenty-one iterations, starting with the loop label, Loop. We then keep the value of A, PUSH AF; the next instruction, RST $10, prints the character to which the code loaded into A belongs and modifies the register. We recover the value of A, POP AF.
We then increment A, INC A, so that it points to the next character, decrement B and jump to the loop if it has not reached zero, DJNZ Loop. Finally, we return to Basic, RET.
The last line tells PASMO to include the call to the address of the Main tag in the Basic loader.
Now it’s time to compile and see the results in the emulator.
pasmo --name Martian --tapbas main.asm martian.tap martian.log
But have we painted our graphics?
We have painted the capital letters A to U because we have not indicated where our graphics are.
Next, we go to the main.asm file, and under the Main tag we add the following lines:
ld hl, udgsCommon
ld (UDG), hl
We load into HL the address where the graphics are, LD HL, udgsCommon, and load that value into the address where the location of our graphics is, LD (UDG), HL.
As both udgsCommon and UDG are not defined in the main.asm file, we need to add the includes for the const.asm and var.asm files after the RET statement.
include "const.asm"
include "var.asm"
Now we can recompile the program, load it into the emulator and see our graphics on the screen.
Much better, right? But we painted twenty-one graphics: the ship, the shot, the explosion, the frame, the blank character, the graphics of enemy one and part of the graphics of enemy two. How are we going to paint the other two graphics of enemy two and the rest?
We load the enemies’ UDGs
Looking at the graphics definition, the first tag is called udgsCommon, and this should give us a clue as to how we’re going to do this. We have defined fifteen common UDGs (ship, shot, explosion, frame and target), so we are going to define thirty-two bytes to be able to dump the enemy graphics into them; we do this because the enemies are one per level, and the dump is only done once, just at the level change.
In the var.asm file, above udgsEnemiesLeve1, let’s add the following lines:
udgsExtension:
db $00, $00, $00, $00, $00, $00, $00, $00 ; $9f Left/Up
db $00, $00, $00, $00, $00, $00, $00, $00 ; $a0 Right/Up
db $00, $00, $00, $00, $00, $00, $00, $00 ; $a1 Left/Down
db $00, $00, $00, $00, $00, $00, $00, $00 ; $a2 Right/Down
In this block of memory we are going to dump the graphics of the enemies, depending on the level we are in.
Try compiling now and see how it looks. The enemy graphics are gone, aren’t they? It’s the udgsExtension painting.
We create the file graph.asm and implement in it the routine that loads into udgsExtension the graphics of the enemies of each of the levels, data that it receives in A.
To calculate the address where the graphics are, we multiply the level by thirty-two (bytes occupied by the graphics) and add the result to the address where the graphics of the first enemy are.
LoadUdgsEnemies:
dec a
ld h, $00
ld l, a
Since the levels range from one to thirty, we decrement A, DEC A, so that it does not add one level too many (level one adds zero to udgsEnemies, level two adds one, etc.).
The next step is to load the level into HL, for which we load zero into H, LD H, $00, and the level into L, LD L, A.
add hl, hl
add hl, hl
add hl, hl
add hl, hl
add hl, hl
We multiply the level by thirty-two by adding HL to itself five times, ADD HL, HL. The first addition is like multiplying by two, the second by four, by eight, by sixteen and by thirty-two.
ld de, udgsEnemiesLevel1
add hl, de
ld de, udgsExtension
ld bc, $20
ldir
ret
We load the address of the first enemy graphic into DE, LD DE, udgsEnemiesLevel1, and add it to HL, ADD HL, DE. We load the extension address DE, LD DE, udgsExtension, load the number of bytes we are going to load into udgsExtension, LD BC, $20, and load the thirty-two bytes of the level’s enemy graphics into udgsExtension, LDIR. Finally we exit, RET.
The final aspect of the routine is as follows:
; -------------------------------------------------------------------
; Load user-defined graphics relating to enemies
;
; Entry: A -> Level from 1 to 30
;
; Alters the value of the A, BC, DE and HL registers.
; -------------------------------------------------------------------
LoadUdgsEnemies:
dec a ; A = A - 1 which does not add one level more
ld h, $00
ld l, a ; HL = level
add hl, hl ; HL = HL * 2
add hl, hl ; * 4
add hl, hl ; * 8
add hl, hl ; * 16
add hl, hl ; * 32
ld de, udgsEnemiesLevel1 ; DE = address enemy graphics 1
add hl, de ; HL = HL + DE
ld de, udgsExtension ; DE = extension address
ld bc, $20 ; BC = bytes to copy, 32
ldir ; Copies enemy bytes to extension
ret
Let’s test the new routine by editing the main.asm file, starting by changing the LD B, $15 instruction, above the Loop label, and leaving it as follows; it prints the first fifteen UDGs, the common ones:
ld b, $0f
The rest is implemented between the DJNZ Loop instruction and the RET instruction.
ld a, $01
ld b, $1e
We load in A the first level, LD A, $01, in B the total number of levels (thirty), LD B, $1E, and implement a loop that draws the enemies of the thirty levels.
Loop2:
push af
push bc
call LoadUdgsEnemies
We keep AF, PUSH AF, and BC, PUSH BC, as we use A and B to control the enemies we paint and the loop iterations. Next, we call the routine that loads the level’s enemy graphics into udgsExtension, CALL LoadUdgsEnemies.
ld a, $9f
rst $10
ld a, $a0
rst $10
ld a, $a1
rst $10
ld a, $a2
rst $10
The characters corresponding to the enemy graphics are $9F, $A0, $A1 and $A2; we load them into A, LD A, $9F, and paint, RST $10. We repeat the process with $A0, $A1 and $A2.
pop bc
pop af
inc a
djnz Loop2
Recover BC, POP BC, AF, POP AF, increment A to go to the next level, INC A, and repeat until B is zero, DJNZ Loop2.
Finally, at the end of the file and before END Main, we insert the file graph.asm.
include "graph.asm"
The final main.asm code is as follows:
org $5dad
Main:
ld hl, udgsCommon
ld (UDG), hl
ld a, $90
ld b, $0f
Loop:
push af
rst $10
pop af
inc a
djnz Loop
ld a, $01
ld b, $1e
Loop2:
push af
push bc
call LoadUdgsEnemies
ld a, $9f
rst $10
ld a, $a0
rst $10
ld a, $a1
rst $10
ld a, $a2
rst $10
pop bc
pop af
inc a
djnz Loop2
ret
include "const.asm"
include "graph.asm"
include "var.asm"
end Main
We compile, load into the emulator and see that we have painted all our graphics.
ZX Spectrum Assembly, Space Battle
At this point we have defined all the graphics and learned how to paint them.
In the next chapter of ZX Spectrum Assembly, we will paint the play area.
Download the source code from here.
Useful links
ZX Spectrum Assembly, Space Battle by Juan Antonio Rubio García.
Translation by Felipe Monge Corbalán.
This work is licensed to Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0).
Any comments are always welcome.