Home > Z80 Assembly > How To Write ZX Spectrum Games – Chapter 5

How To Write ZX Spectrum Games – Chapter 5

Simple Background Collision Detection

Note: This article was originally written by Jonathan Cauldwell and is reproduced here with permission.

Finding Attributes

Anyone who ever spent time programming in Sinclair BASIC may well remember the ATTR function. This was a way to detect the colour attributes of any particular character cell on the screen, and though tricky for the BASIC programmer to grasp, could be very handy for simple collision detection. The method was so useful in fact that it its machine language equivalent was employed by a number of commercial games, and it is of great use to the novice Spectrum programmer.

There are two ways to find the colour attribute settings for a particular character cell on the Spectrum. A quick look through the Spectrum’s ROM disassembly reveals a routine at address 9603 which will do the job for us, or we can calculate the memory address ourselves.

The simplest way to find an attribute value is to use a couple of ROM routines:

       ld bc,(ballx)       ; put x and y in bc register pair.
       call 9603           ; call ROM to put attribute (c,b) on stack.
       call 11733          ; put attributes in accumulator.

However, it is much faster to do the calculation ourselves. It is also useful to calculate an attribute’s address, and not just its value, in case we want to write to it as well.

Calculating Attribute Addresses

Unlike the Spectrum’s awkward pixel layout, colour cells, located at addresses 22528 to 23295 inclusive, are arranged sequentially in RAM as one would expect. In other words, the screen’s top 32 attribute cells are located at addresses 22528 to 22559 going left to right, the second row of colour cells from 22560 to 22591 and so on. To find the address of a colour cell at print position (x,y) we therefore need only to multiply x by 32, add y, then add 22528 to the result. By then examining the contents of this address we can find out the colours displayed at a particular position, and act accordingly. The following example calculates the address of an attribute at character position (b,c) and returns it in the HL register pair.

; Calculate address of attribute for character at (b, c).

atadd  ld a,b              ; x position.
       rrca                ; multiply by 32.
       rrca
       rrca
       ld l,a              ; store away in l.
       and 3               ; mask bits for high byte.
       add a,88            ; 88*256=22528, start of attributes.
       ld h,a              ; high byte done.
       ld a,l              ; get x*32 again.
       and 224             ; mask low byte.
       ld l,a              ; put in l.
       ld a,c              ; get y displacement.
       add a,l             ; add to low byte.
       ld l,a              ; hl=address of attributes.
       ld a,(hl)           ; return attribute in a.
       ret

Interrogating the contents of the byte at hl will give the attribute’s value, while writing to the memory location at hl will change the colour of the square.

To make sense of the result we have to know that each attribute is made up of 8 bits which are arranged in this manner:

d0-d2		ink colour 0-7,			0=black, 1=blue, 2=red, 3=magenta,
						4=green, 5=cyan, 6=yellow, 7=white
d3-d5		paper colour 0-7,		0=black, 1=blue, 2=red, 3=magenta,
						4=green, 5=cyan, 6=yellow, 7=white
d6		bright,				0=dull, 1=bright
d7		flash,				0=stable, 1=flashing

 

The test for green paper for example, might involve:

       and 56              ; mask away all but paper bits.
       cp 32               ; is it green(4) * 8?
       jr z,green          ; yes, do green thing.

while checking for yellow ink could be done like this:

       and 7               ; only want bits pertaining to ink.
       cp 6                ; is it yellow (6)?
       jr z,yellow         ; yes, do yellow wotsit.

Applying what we Have Learned to the Game

We can now add an attribute collision check to our Centipede game. As before, the new sections are underlined.

; We want a black screen.

       ld a,71             ; white ink (7) on black paper (0),
                           ; bright (64).
       ld (23693),a        ; set our screen colours.
       xor a               ; quick way to load accumulator with zero.
       call 8859           ; set permanent border colours.

; Set up the graphics.

       ld hl,blocks        ; address of user-defined graphics data.
       ld (23675),hl       ; make UDGs point to it.

; Okay, let's start the game.

       call 3503           ; ROM routine - clears screen, opens chan 2.

; Initialise coordinates.

       ld hl,21+15*256     ; load hl pair with starting coords.
       ld (plx),hl         ; set player coords.

       call basexy         ; set the x and y positions of the player.
       call splayr         ; show player base symbol.

; Now we want to fill the play area with mushrooms.

       ld a,68             ; green ink (4) on black paper (0),
                           ; bright (64).
       ld (23695),a        ; set our temporary colours.
       ld b,50             ; start with a few.
mushlp ld a,22             ; control code for AT character.
       rst 16
       call random         ; get a 'random' number.
       and 15              ; want vertical in range 0 to 15.
       rst 16
       call random         ; want another pseudo-random number.
       and 31              ; want horizontal in range 0 to 31.
       rst 16
       ld a,145            ; UDG 'B' is the mushroom graphic.
       rst 16              ; put mushroom on screen.
       djnz mushlp         ; loop back until all mushrooms displayed.

; This is the main loop.

mloop  equ $

; Delete the player.

       call basexy         ; set the x and y positions of the player.
       call wspace         ; display space over player.

; Now we've deleted the player we can move him before redisplaying him
; at his new coordinates.

       ld bc,63486         ; keyboard row 1-5/joystick port 2.
       in a,(c)            ; see what keys are pressed.
       rra                 ; outermost bit = key 1.
       push af             ; remember the value.
       call nc,mpl         ; it's being pressed, move left.
       pop af              ; restore accumulator.
       rra                 ; next bit along (value 2) = key 2.
       push af             ; remember the value.
       call nc,mpr         ; being pressed, so move right.
       pop af              ; restore accumulator.
       rra                 ; next bit (value 4) = key 3.
       push af             ; remember the value.
       call nc,mpd         ; being pressed, so move down.
       pop af              ; restore accumulator.
       rra                 ; next bit (value 8) reads key 4.
       call nc,mpu         ; it's being pressed, move up.

; Now he's moved we can redisplay the player.

       call basexy         ; set the x and y positions of the player.
       call splayr         ; show player.

       halt                ; delay.

; Jump back to beginning of main loop.

       jp mloop

; Move player left.

mpl    ld hl,ply           ; remember, y is the horizontal coord!
       ld a,(hl)           ; what's the current value?
       and a               ; is it zero?
       ret z               ; yes - we can't go any further left.

; now check that there isn't a mushroom in the way.

       ld bc,(plx)         ; current coords.
       dec b               ; look 1 square to the left.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushrooms are bright (64) + green (4).
       ret z               ; there's a mushroom - we can't move there.

       dec (hl)            ; subtract 1 from y coordinate.
       ret

; Move player right.

mpr    ld hl,ply           ; remember, y is the horizontal coord!
       ld a,(hl)           ; what's the current value?
       cp 31               ; is it at the right edge (31)?
       ret z               ; yes - we can't go any further left.

; now check that there isn't a mushroom in the way.

       ld bc,(plx)         ; current coords.
       inc b               ; look 1 square to the right.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushrooms are bright (64) + green (4).
       ret z               ; there's a mushroom - we can't move there.

       inc (hl)            ; add 1 to y coordinate.
       ret

; Move player up.

mpu    ld hl,plx           ; remember, x is the vertical coord!
       ld a,(hl)           ; what's the current value?
       cp 4                ; is it at upper limit (4)?
       ret z               ; yes - we can go no further then.

; now check that there isn't a mushroom in the way.

       ld bc,(plx)         ; current coords.
       dec c               ; look 1 square up.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushrooms are bright (64) + green (4).
       ret z               ; there's a mushroom - we can't move there.

       dec (hl)            ; subtract 1 from x coordinate.
       ret

; Move player down.

mpd    ld hl,plx           ; remember, x is the vertical coord!
       ld a,(hl)           ; what's the current value?
       cp 21               ; is it already at the bottom (21)?
       ret z               ; yes - we can't go down any more.

; now check that there isn't a mushroom in the way.

       ld bc,(plx)         ; current coords.
       inc c               ; look 1 square down.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushrooms are bright (64) + green (4).
       ret z               ; there's a mushroom - we can't move there.

       inc (hl)            ; add 1 to x coordinate.
       ret

; Set up the x and y coordinates for the player's gunbase position,
; this routine is called prior to display and deletion of gunbase.

basexy ld a,22             ; AT code.
       rst 16
       ld a,(plx)          ; player vertical coord.
       rst 16              ; set vertical position of player.
       ld a,(ply)          ; player's horizontal position.
       rst 16              ; set the horizontal coord.
       ret

; Show player at current print position.

splayr ld a,69             ; cyan ink (5) on black paper (0),
                           ; bright (64).
       ld (23695),a        ; set our temporary screen colours.
       ld a,144            ; ASCII code for User Defined Graphic 'A'.
       rst 16              ; draw player.
       ret

wspace ld a,71             ; white ink (7) on black paper (0),
                           ; bright (64).
       ld (23695),a        ; set our temporary screen colours.
       ld a,32             ; SPACE character.
       rst 16              ; display space.
       ret

; Simple pseudo-random number generator.
; Steps a pointer through the ROM (held in seed), returning
; the contents of the byte at that location.

random ld hl,(seed)        ; Pointer
       ld a,h
       and 31              ; keep it within first 8k of ROM.
       ld h,a
       ld a,(hl)           ; Get "random" number from location.
       inc hl              ; Increment pointer.
       ld (seed),hl
       ret
seed   defw 0

; Calculate address of attribute for character at (dispx, dispy).

atadd  ld a,c              ; vertical coordinate.
       rrca                ; multiply by 32.
       rrca                ; Shifting right with carry 3 times is
       rrca                ; quicker than shifting left 5 times.
       ld e,a
       and 3
       add a,88            ; 88x256=address of attributes.
       ld d,a
       ld a,e
       and 224
       ld e,a
       ld a,b              ; horizontal position.
       add a,e
       ld e,a              ; de=address of attributes.
       ld a,(de)           ; return with attribute in accumulator.
       ret

plx    defb 0              ; player's x coordinate.
ply    defb 0              ; player's y coordinate.

; UDG graphics.

blocks defb 16,16,56,56,124,124,254,254    ; player base.
       defb 24,126,255,255,60,60,60,60     ; mushroom.
Advertisements
  1. August 19, 2013 at 10:41 pm

    I really like your blog entries about the ZX. Keep up the good job and thank you for doing this! Keep the Spectrum alive!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: