Espamática
ZX SpectrumRetroZ80 Assembly

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

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:

ASM
; 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:

ASM
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.

ShellScript
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:

ASM
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.

ASM
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:

ASM
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.

ASM
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.

ASM
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.

ASM
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:

ASM
; -------------------------------------------------------------------
; 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:

ASM
ld   b, $0f

The rest is implemented between the DJNZ Loop instruction and the RET instruction.

ASM
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.

ASM
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.

ASM
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.

ASM
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.

ASM
include "graph.asm"

The final main.asm code is as follows:

ASM
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.

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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies

Descubre más desde Espamática

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo