¡Hola a todos!, hay problemas con la web que espero poder arreglar lo antes posible, ¡mil perdones!

Ver índice de webs/blogs

Espamatica : ZX Spectrum Assembly, Space Battle – 0x02 Painting UDG Leer


In this chapter of ZX Spectrum Assembly, we will start drawing with UDG.

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?

ZX Spectrum Assembly, Space Battle
ZX Spectrum Assembly, Space Battle

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.

ZX Spectrum Assembly, Space Battle
ZX Spectrum Assembly, Space Battle

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
ZX Spectrum Assembly, Space Battle

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.