The Egghead and Haunted House Site

Home

Downloads
History/Background
Writing Spectrum Games
Programmer's Log
Related Links
About the Author
Guestbook
Writing Spectrum Games


Fancy Writing Your Own Games?

It's been a couple of years in the making and it's still years from completion, but a number of chapters of my guide to programming Spectrum games are now in a readable state. The finished document will ultimately be much larger - but for now it should cover a few basics. This is version 0.2, updated with a couple of new chapters and other improvements.

Click here to download Wordpad document.



We shall continue with a very basic golf game which uses the calculator routines in the Spectrum's ROM. It's very slow, and a poor game by anyone's standards, but it may give you a few pointers to handy ROM routines. If you're feeling adventurous, you may like to write a title page, and even implement sound effects and a "low score" feature. Have fun, and if you need help with this listing don't hesitate to contact me!

; Golf.
; (c)2004 Jonathan Cauldwell.
; A simple golf game to demonstrate use of the calculator stack.
; Project started      14.02.2004 @ 17:23.
; Project completed    22.02.2004 @ 20:38
; Definitions of constants.
TREEAT equ 4               ; green (+4) ink on black (+0) paper.
FAIRAT equ 33              ; blue (+1) ink on green (+32) paper.
GRENAT equ 68              ; bright (+64) green (+4) ink on black (+0) paper.
HOLEAT equ 96              ; black (+0) ink on bright (+64) green (+32) paper.
WATRAT equ 41              ; blue (+1) ink on cyan (+40) paper.
; Start address to which we're compiling.
       org 24000
       ld hl,udgs          ; character graphics.
       ld (23675),hl       ; point UDGs at them.
       xor a               ; zeroise accumulator.
       ld (shot),a         ; number of shots taken.
nhole  ld hl,23693         ; screen attributes.
       ld (hl),32          ; black ink (+0) on green (+32) paper.
       inc hl              ; next byte = mask (ie ink/paper 8).
       ld (hl),0           ; no colour masking.
       call 3503           ; clear screen.
; Display rough.  This consists of 32 x 16 = 512 UDG A characters.
       ld b,0              ; 256 times (256*2 = 512 chars).
drough ld a,144            ; ASCII code for UDG A (rough).
       rst 16              ; display it.
       ld a,144            ; let's have another one.
       rst 16              ; display this one.
       djnz drough         ; repeat 256 times.
; Set seed for random number generator based on hole.
;      ld a,(hole)         ; hole number.
;      ld l,a
;      ld h,0
;      ld (seed),hl        ; set seed.
; Set colours for trees.
       ld a,TREEAT         ; green (+4) ink on black (+0) paper.
       ld (23695),a        ; set temporary attributes.
; How many trees on this hole?
       call random         ; get random number.
       and 63              ; want result 0-127.
       add a,192           ; minimum of 192 trees.
       ld b,a              ; loop control variable.
; Scatter trees around the field.
; Set coordinates for each tree.
sctree ld a,22             ; ASCII code for coordinate control.
       rst 16              ; next two bytes will determine coordinates.
       call random         ; random vertical coord.
       and 15              ; want result 0-15.
       rst 16              ; set vertical coordinate.
       call random         ; random vertical coord.
       and 31              ; want result 0-31.
       rst 16              ; set horizontal coordinate.
; Display tree.
       ld a,145            ; UDG B = tree.
       rst 16              ; display tree.
       djnz sctree         ; repeat until all trees on screen.
; Set attributes for fairway.  There's no ink so we can set this to
; a colour of our choice to make it easier for detection later.
       ld a,FAIRAT         ; blue (+1) ink on green (+32) paper.
       ld (23695),a        ; set attributes.
; Draw the fairway.
       ld b,30             ; y coords 1-30.
dfairw push bc             ; strip counter.
       call random         ; random number.
       and 3               ; want number 0-3.
       add a,2             ; add 2 so in range 2-5.
       ld (tempx),a        ; store starting coordinate.
       call random         ; get a random number.
       and 3               ; adjust to 0-3.
       add a,5             ; add 5 to give result 5-8.
       ld b,a              ; length of strip.
fstrip ld a,22             ; ASCII code for coordinate control.
       rst 16              ; next two bytes will determine coordinates.
       ld a,(tempx)        ; x (vertical) coordinate.
       inc a               ; move it down.
       ld (tempx),a        ; store current position ready for next iteration.
       rst 16              ; set coordinate.
       pop af              ; strip counter is on the stack so pop it into A.
       push af             ; mustn't forget to put it back, we need it later.
       rst 16              ; set horizontal coordinate.
       ld a,32             ; ASCII code for a space.
       rst 16              ; display chunk of fairway.
       djnz fstrip         ; next bit of fairway strip.
       pop bc              ; restore strip counter.
       djnz dfairw         ; draw next vertical strip.
; Set colours for the water hazard.
       ld a,WATRAT         ; bright (+64) green (+4) ink on black (+0) paper.
       ld (23695),a        ; set attributes.
       call random         ; pseudo-random number.
       and 7               ; make it in range 0-7.
       add a,4             ; add 4 so it's 4-11.
       ld (tempx),a        ; this is our x coord.
       call random         ; pseudo-random number.
       and 7               ; make it in range 0-7.
       add a,12            ; add 12 so it's 4-11.
       ld (tempy),a        ; this is our x coord.
       ld c,3              ; height of water hazard.
water1 ld a,22             ; ASCII code for coordinate control.
       rst 16              ; next two bytes will determine coordinates.
       ld hl,tempx         ; x coord.
       ld a,(hl)           ; get current position.
       inc (hl)            ; increment for next row.
       rst 16              ; set vertical position.
       ld a,(tempy)        ; x coord.
       rst 16              ; set horizontal position.
       ld b,4              ; width of water = 4 chars.
water0 ld a,146            ; ASCII code for UDG C (water).
       rst 16              ; display.
       djnz water0
       dec c               ; height counter.
       jr nz,water1        ; repeat until height is okay.
; Set colours for the putting green.
       ld a,GRENAT         ; bright (+64) green (+4) ink on black (+0) paper.
       ld (23695),a        ; set attributes.
; Draw the green.
       call random         ; random number.
       and 7               ; want it in range 0-7.
       add a,4             ; minimum x coordinate.
       ld (tempx),a        ; store x position.
       ld b,4              ; 'height' (okay, width) of green.
gstrip ld a,22             ; ASCII code for coordinate control.
       rst 16              ; next two bytes will determine coordinates.
; Odd/even holes start on opposite sides of the screen.
       ld a,(tempx)        ; this x coord.
       inc a               ; move it down.
       ld (tempx),a        ; store current position ready for next iteration.
       rst 16              ; set x.
       ld a,(hole)         ; which hole are we on?
       and 1               ; odd/even hole number.
       ld d,30             ; default tee position = right edge.
       ld e,a              ; horizontal starting point.
       jr z,greenl         ; green starting point on the left.
       ld d,1              ; tee position = left edge.
       ld e,24             ; starting point = right of screen.
greenl call random         ; random horizontal element.
       and 3               ; 0-3.
       add a,e             ; y coordinate.
       rst 16
       ld c,5              ; 5 squares 'length' of green.
glenth ld a,143            ; 143=ASCII code for inverted space.
       rst 16              ; plonk green here.
       dec c               ; length counter.
       jr nz,glenth        ; repeat until this strip of green complete.
       djnz gstrip         ; repeat for next strip of green.
; Finally the hole itself - set colours up first.
       ld a,HOLEAT         ; black (+0) ink on bright (+64) green (+32) paper.
       ld (23695),a        ; set attributes.
       ld a,22             ; ASCII code for coordinate control.
       rst 16              ; tell display routines we're setting coords.
       call random         ; random displacement.
       and 3               ; need in range 0-3.
       inc a               ; make it 1-4.
       ld b,a              ; hold in b for now.
       ld a,(tempx)        ; x coord of bottom of green.
       sub b               ; subtract displacement.
       rst 16              ; set vertical (x) coordinate.
       call random         ; random y coord wanted.
       and 3               ; 0-3.
       inc a               ; range 1-4.
       add a,e             ; add to starting coordinate.
       rst 16              ; set y position.
       ld a,'o'            ; use lowercase 'o' for hole graphic.
       rst 16              ; show it.
; Now for the tee position.
       call random
       and 7               ; 0-7.
       add a,5             ; make result 5-12.
       ld e,a
       ld (ballx),de
; Now we've drawn the screen save a copy away so we can redisplay it later.
       ld hl,16384         ; address of screen display.
       ld de,49152         ; place where it's going to be stored.
       ld bc,4096          ; size of top two thirds = 4k.
       ldir                ; transfer it there.
       ld hl,22528         ; now for the attributes.
       ld bc,512           ; top 16 lines occupy 512 bytes.
       ldir                ; copy them to RAM.
; Show status and increment shot number.
       call shotup         ; first shot - display.
; Set up 5-byte form of x and y coordinates for the ball position.
       ld a,(ballx)        ; ball x.
       call 11560          ; stack it.
       call 11249          ; transfer stack to aedcb.
       call putbfx         ; store floating point x coord.
       ld a,(bally)        ; ball y coordinate.
       call 11560          ; stack it.
       call 11249          ; transfer stack to aedcb.
       call putbfy         ; store floating point y coordinate.
; Now set limits for cursor position.
setlim ld a,(ballx)        ; ball x position.
       ld b,0              ; default minimum x coordinate.
       ld c,21             ; default maximum.
       sub 3               ; 3 chars above = top.
       jr c,xtopok         ; that's negative, so use default.
       ld b,a              ; this is top limit.
xtopok ld a,(ballx)        ; ball vertical.
       ld (cursx),a        ; set cursor x.
       add a,3             ; 3 lines down = x maximum.
       cp 16               ; have we reached physical limit?
       jr nc,xbotok        ; yes, so use default instead.
       ld c,a              ; this is bottom limit.
xbotok ld a,b              ; highest point.
       ld (cursu0+1),a     ; set limit for comparison.
       ld a,c              ; lowest point.
       ld (cursd0+1),a     ; set limit for comparison.
       ld a,(bally)        ; ball y position.
       ld b,0              ; default minimum y coordinate.
       ld c,31             ; default maximum.
       sub 3               ; 3 chars to left.
       jr c,ylftok         ; that's negative, so use default.
       ld b,a              ; this is left limit.
ylftok ld a,(bally)        ; ball horizontal.
       ld (cursy),a        ; set cursor y.
       add a,3             ; 3 lines across = y maximum.
       cp 32               ; have we reached physical limit?
       jr nc,yrgtok        ; yes, so use default instead.
       ld c,a              ; this is bottom limit.
yrgtok ld a,b              ; leftmost point.
       ld (cursl0+1),a     ; set limit for comparison.
       ld a,c              ; rightmost point.
       ld (cursr0+1),a     ; set limit for comparison.
; Main control loop.
move   call disbal         ; show the ball.
; Show the cursor.
       ld a,22             ; ASCII code for coordinate control.
       rst 16              ; tell display routines we're setting coords.
       ld a,(cursx)        ; golf ball x position.
       rst 16              ; set it.
       ld a,(cursy)        ; ball y coordinate.
       rst 16              ; set it.
       ld a,'+'            ; cursor.
       rst 16
; Reset last keypress.
       xor a
       ld (23560),a
; Check for keypress and move cursor accordingly.
move0  ld a,(23560)
       and a               ; anything pressed?
       jr z,move0          ; no.
       call restsc         ; restore screen.
       cp 'q'              ; up pressed.
       jp z,cursu
       cp 'a'              ; down key.
       jp z,cursd
       cp 'o'              ; cursor left.
       jp z,cursl
       cp 'p'              ; right key.
       jp z,cursr
       cp 'm'              ; play the ball.
       jp nz,move          ; not pressed so loop until it is.
; Okay, we're ready to shoot.  Make sure cursor is not positioned over
; the ball or we won't know which direction we're pointing at.
valid  ld a,(cursx)        ; cursor x position.
       ld hl,ballx         ; ball x coordinate.
       cp (hl)             ; are x coords the same?
       jr nz,firep         ; no, go to firepower section.
       ld a,(cursy)        ; cursor y.
       inc hl              ; ball y coordinate.
       cp (hl)             ; are they identical?
       jr z,move           ; yes they are - don't allow shot.
; Establish how much power the player wants.
firep  ld hl,power
       ld (hl),1
fire   ld d,90             ; 90 x 256 = 23040, bottom third of attributes.
       ld a,(hl)           ; power setting.
       add a,31            ; line 17.
       ld e,a              ; de=attribute of next bit of power bar.
       ld a,23             ; white ink on red paper.
       ld (de),a           ; set colour of bar.
       call delay          ; want a short delay.
       ld bc,32766         ; port for keyboard row B to SPACE.
       in a,(c)            ; see what's pressed.
       and 4               ; key 'm'=bit d2.
       jr nz,calcs         ; key released - do angle calculations.
       ld a,(hl)           ; current power setting.
       cp 32               ; reached max already?
       jr z,fire           ; yes - can't increase, wait for key release.
       inc (hl)            ; increase power.
       jr fire             ; repeat until key released.
; Right, we need to find the angle and to do this we divide the adjacent by
; the hypotenuse and find the arccosine.
; Firstly we put the length of the adjacent on the stack:
calcs  call stxlen         ; stack x coords of cursor and ball.
       rst 40              ; perform a calculation.
       defb 3              ; find difference.
       defb 42             ; make it positive.
       defb 56             ; calculation done.
; Next we require the length of the hypotenuse and we can use good
; old Pythagoras' theorem for this.
       call stxlen         ; stack x coords of cursor and ball.
       call sqdiff         ; find the difference between them and square it.
       ld a,(cursy)        ; cursor x coordinate.
       call 11560          ; place it on the calculator stack.
       ld a,(bally)        ; ball x coordinate.
       call 11560          ; place it on the calculator stack.
       call sqdiff         ; find the difference between them and square it.
       rst 40              ; call calculator.
       defb 15             ; want the sum.
       defb 40             ; find square root.
; Adjacent and hypotenuse are now on the calculator stack.
; We now divide the first by the second and find the arccosine.
; Remember - cosine = adjacent over hypotenuse.
       defb 5              ; perform division.
       defb 35             ; want the arccosine.
       defb 56             ; calculation done.
; Okay, we have the angle but it's only 0 to half-pi radians (90 degrees)
; so we need to make an adjustment based upon the quarter of the circle.
; We can establish which quarter of the circle our angle lies in by
; examining the differences between the ball and cursor coordinates.
; Difference between ball and cursor x coordinates tells us which half.
       ld hl,cursx         ; cursor x coord.
       ld a,(ballx)        ; ball x coord.
       cp (hl)             ; compare the two.
       jr c,half2          ; cursor is lower, angle in bottom half.
; We're in the top half of the circle then.
       inc hl              ; cursor y.
       ld a,(bally)        ; ball y position.
       cp (hl)             ; is cursor to the right?
       jr nc,quar2         ; no it isn't, angle in second quarter.
; Angle to play is in first quarter, so it needs subtracting from pi/2.
       rst 40              ; call ROM calculator.
       defb 163            ; stack half-pi.
       defb 1              ; swap values around.
       defb 3              ; subtract from half-pi.
       defb 56             ; calculation done - angle on stack.
       jr play             ; play the shot.
; Second quarter - add literal pi/2 to our angle.
quar2  rst 40              ; call the calculator.
       defb 163            ; stack a half-pi.
       defb 15             ; want the sum.
       defb 56             ; done.
       jr play             ; play the shot.
       inc hl              ; cursor y.
       ld a,(bally)        ; ball y position.
       cp (hl)             ; is cursor to the right?
       jr c,quar2          ; no it isn't, angle in second quarter.
; Second half - we either subtract angle from 3*pi/2 or add to it.
half2  ld a,3              ; value 3.
       call 11560          ; stack it.
; Now see if we're in the third or fourth quarter.
       ld hl,cursy         ; cursor y.
       ld a,(bally)        ; ball y position.
       cp (hl)             ; is cursor to the right?
       jr c,quar4          ; yes it is, angle in final quarter.
; Third quarter - subtract from 3*pi/2.
       rst 40              ; call calculator again.
       defb 163            ; stack half pi.
       defb 4              ; multiply to get 3*pi.
       defb 1              ; exchange top 2 values.
       defb 3              ; subtract result.
       defb 56             ; done for now.
       jr play             ; play on.
; Fourth quarter, add our angle to 3*pi/2.
quar4  rst 40              ; call calculator again.
       defb 163            ; stack pi.
       defb 4              ; multiply to get 3*pi/2..
       defb 15             ; find the sum.
       defb 56             ; done for now.
; Now we know the angle we can find the x and y coordinate displacements
; using sine and cosine respectively.
play   rst 40              ; call calculator.
       defb 49             ; make duplicate copy of angle.
       defb 31             ; find sine.
       defb 1              ; switch top values.
       defb 32             ; find cosine.
       defb 56             ; finished for now.
       call 11249          ; transfer sine to aedcb registers.
       ld (ystep),a        ; store y step.
       ld (ystep+1),de
       ld (ystep+3),bc
       call 11249          ; transfer sine to aedcb registers.
       ld (xstep),a        ; store x step.
       ld (xstep+1),de
       ld (xstep+3),bc
play0  call getbfx         ; get ball floating point x coord.
       call 10934          ; stack x coord.
       ld a,(xstep)        ; restore x step.
       ld de,(xstep+1)
       ld bc,(xstep+3)
       call 10934          ; stack x step.
       rst 40              ; call calculator.
       defb 3              ; subtract step from coord.
       defb 49             ; make duplicate copy of coordinate.
       defb 55             ; check > 0.
       defb 56             ; end calculation.
       call 11733          ; take sign.
       and a               ; is it negative?
       jp z,penal0         ; yes, it's out of bounds.
       call 11249          ; put result in aedcb.
       call putbfx         ; store ball x.
       call getbfy         ; get ball floating point y coord.
       call 10934          ; stack y coord.
       ld a,(ystep)        ; restore x step.
       ld de,(ystep+1)
       ld bc,(ystep+3)
       call 10934          ; stack x step.
       rst 40              ; call calculator.
       defb 15             ; add together.
       defb 49             ; make duplicate copy of coordinate.
       defb 55             ; check > 0.
       defb 56             ; end calculation.
       call 11733          ; take sign.
       and a               ; is it negative?
       jr z,penal0         ; yes, it's out of bounds.
       call 11249          ; put result in aedcb.
       call putbfy         ; store floating point y coord.
; Now compress floating-point form coordinates into 8-bit values for display.
       call getbfx         ; get ball floating point x coord.
       call 10934          ; stack x coord.
       call 11733          ; want result in accumulator.
       cp 16               ; beyond lowest edge of window?
       jr nc,penal         ; certainly is - out of bounds.
       ld (ballx),a        ; ball x coord.
       call getbfy         ; put ball y coord in aedcb.
       call 10934          ; stack coordinate.
       call 11733          ; want result in accumulator.
       cp 32               ; beyond right edge of screen?
       jr nc,penal         ; certainly is - out of bounds.
       ld (bally),a        ; ball y coord.
       call restsc         ; display course.
; Before we display the golf ball we'll check the attributes here.
       ld bc,(ballx)       ; put x and y in bc register pair.
       call 9603           ; ROM routine to put attribute (c,b) on stack.
; Redisplay ball.
       call disbal         ; display ball at new position.
       call delay          ; short delay.
; Our ROM call stored the screen attributes on the stack so let's
; examine them and act accordingly.
       call 11733          ; put attributes in accumulator.
       cp HOLEAT           ; are we over the hole?
       jp z,holed          ; yes - check speed and drop in if appropriate.
       cp TREEAT           ; are we over a tree?
       jp z,htree          ; check if we hit it.
       cp WATRAT           ; are we over water?
       jp z,hwatr          ; check if we landed in it.
; Ball is safe here so store coordinates.
       ld hl,(ballx)       ; ball coords.
       ld (tempx),hl       ; put here in case we hit water hazard later.
rejoin ld hl,power         ; power.
       dec (hl)            ; decrement it.
       jp nz,play0         ; repeat until no power left.
; Shot complete.
       jr next
; Out of bounds.
penal0 call 11733          ; remove value at top of stack and discard it.
penal  call shotup         ; penalty stroke.
next   call 3503           ; clear screen.
       call restsc         ; redisplay course.
       call shotup         ; add 1 to shot.
       jp setlim
; Has the player holed out?
; Ball is over the hole, but has it been hit too hard?
holed  ld a,(power)        ; remaining power.
       cp 3                ; is there much velocity left?
       jr nc,rejoin        ; plenty - fly straight past.
       ld hl,hole          ; hole number.
       inc (hl)            ; next one please.
       ld a,(hl)           ; which one are we on?
       cp 19               ; the 19th?
       ret z               ; time to hit the bar then!
       jp nhole            ; start a new hole.
htree  call random         ; random number decides if we hit tree.
       cp 70               ; chance of collision/256.
       jp nc,rejoin        ; didn't hit it - carry on.
       jr next             ; hit tree - ball stops dead.
hwatr  ld a,(power)        ; speed left in shot.
       cp 5                ; are we going along ground?
       jr nc,rejoin        ; no we're in the air - sail on by.
; We've landed in the water.
       ld hl,(tempx)       ; get last safe coords.
       ld (ballx),hl       ; put ball on side of bank.
       jr penal            ; incur penalty stroke.
delay  ld b,5
delay0 halt
       djnz delay0
       ret
shotup ld de,holtx0        ; hole number text.
       ld bc,shotx0-holtx0 ; length.
       call 8252           ; display string.
       ld hl,hole          ; hole number.
       call dispno
       ld de,shotx0        ; shots text.
       ld bc,shotx1-shotx0
       call 8252           ; display string.
       ld hl,shot          ; number of shots taken.
       inc (hl)            ; increment it.
dispno ld c,(hl)           ; put result in bc pair.
       ld b,0              ; no high byte.
       jp 6683             ; display shots taken.
holtx0 defb 22,20,2
       defb 'Hole '
shotx0 defb 22,20,13
       defb 'Shot '
shotx1 equ $
stxlen ld a,(cursx)        ; cursor x coordinate.
       call 11560          ; place it on the calculator stack.
       ld a,(ballx)        ; ball x coordinate.
       jp 11560            ; place it on the calculator stack.
sqdiff rst 40              ; perform a calculation.
       defb 3              ; subtraction.
       defb 49             ; duplicate value.
       defb 4              ; multiply together.
       defb 56             ; code for termination.
       ret
getbfy ld a,(ballfy)       ; restore floating point y coordinate.
       ld de,(ballfy+1)
       ld bc,(ballfy+3)
       ret
getbfx ld a,(ballfx)       ; restore floating point format x coord.
       ld de,(ballfx+1)
       ld bc,(ballfx+3)
       ret
putbfx ld (ballfx),a       ; store new x coord.
       ld (ballfx+1),de
       ld (ballfx+3),bc
       ret
putbfy ld (ballfy),a       ; store new y coord.
       ld (ballfy+1),de
       ld (ballfy+3),bc
       ret
cursu  ld hl,cursx         ; coordinate of cursor.
       ld a,(hl)
cursu0 cp 0                ; already at top?
       jr z,cursrs         ; can't go beyond here.
       dec (hl)            ; move cursor.
cursrs jp move             ; rejoin main loop.
cursd  ld hl,cursx         ; coordinate of cursor.
       ld a,(hl)
cursd0 cp 15               ; already at max?
       jr z,cursrs         ; can't go beyond here.
       inc (hl)            ; move cursor.
       jr cursrs           ; rejoin main loop.
cursl  ld hl,cursy         ; coordinate of cursor.
       ld a,(hl)
cursl0 cp 0                ; already at edge?
       jr z,cursrs         ; can't go past here.
       dec (hl)            ; move cursor.
       jr cursrs           ; rejoin main loop.
cursr  ld hl,cursy         ; coordinate of cursor.
       ld a,(hl)
cursr0 cp 31               ; already at edge?
       jr z,cursrs         ; no movement possible.
       inc (hl)            ; move cursor.
       jr cursrs           ; rejoin main loop.
; Display the golf ball.
disbal ld hl,23694         ; mask for colours.
       ld (hl),64          ; 64 = bright.
       inc hl              ; temporary colours.
       ld (hl),39          ; white ink (+7) on green paper (32).
       ld a,22             ; ASCII code for coordinate control.
       rst 16              ; tell display routines we're setting coords.
       ld a,(ballx)        ; golf ball x position.
       rst 16              ; set it.
       ld a,(bally)        ; ball y coordinate.
       rst 16              ; set it.
       ld a,'.'            ; golf ball.
       rst 16
       ret
restsc ld hl,49152         ; address of stored screen.
       ld de,16384         ; physical screen address.
       ld bc,4096          ; size of screen display.
       ldir                ; move top two-thirds in one block.
       ld de,22528         ; attributes.
       ld bc,512           ; 16 x 32 squares.
       ldir                ; transfer those too.
       ret
; 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
       cp 58               ; Approaching end of "random" area?
       jr nc,rand0         ; Yes - reset pointer.
       ld a,(hl)           ; Get "random" number from location.
       inc hl              ; Increment pointer.
       ld (seed),hl
       ret
rand0  ld hl,0             ; Reset pointer.
       ld (seed),hl
       ret
; Graphics - rough, trees and water.
udgs   defb 0,0,80,32,0,0,5,2,247,227,247,193,247,128,247,247
       defb 0,8,62,255,255,247,193,0
; Variables.
hole   defb 0              ; 1 byte.
seed   defw 0              ; 2 bytes.
tempx  defb 0              ; 1 byte.
tempy  defb 0              ; 1 byte.
ballx  defb 0              ; 1 byte.
bally  defb 0              ; 1 byte.
cursx  defb 0              ; 1 byte.
cursy  defb 0              ; 1 byte.
xstep  defb 0,0,0,0,0      ; 5 bytes - ball x steps.
ystep  defb 0,0,0,0,0      ; 5 bytes - ball y steps.
ballfx defb 0,0,0,0,0      ; 5 bytes - ball floating point x.
ballfy defb 0,0,0,0,0      ; 5 bytes - ball floating point y.
power  defb 0              ; 1 byte - power.
shot   defb 0              ; 1 byte - number of shots.




This listing is a simple 1K version of Centipede using UDG graphics and the the ROM PRINT routines. In fact, it's the source code for Kilopede, a program I entered in the 1K category for the 2003 minigame programming competition.


; Kilopede.
; (c)2003 Jonathan Cauldwell.
; An entry for the 2003 Minigame Competition, 1k category.
; Project started      18.07.2003 @ 14:00.
; Project completed    18.07.2003 @ 19:39.
; Start address to which we're compiling.
; As we're working in 1k let's locate it in a REM statement.
       org 23770
; We want a black screen.
       ld a,71             ; white on black, bright.
       ld (23693),a        ; screen colours.
       ld (23624),a        ; permanent border colours.
       inc a
       out (254),a         ; temporary border colour.
; Set up the graphics.
       ld hl,blocks        ; address of user-defined graphics data.
       ld (23675),hl       ; make UDGs point to it.
; Any key to start game.
restrt ld hl,23560         ; last keypress.
       ld (hl),l
rstrt0 ld a,(hl)           ; keypress?
       cp l
       jr z,rstrt0         ; no, wait until something pressed.
; Okay, let's start the game.
       call 3503           ; ROM routine - clears screen and opens chan #2.
; Number of lives.
       ld a,3              ; We'll start with three lives.
       ld (lives),a
       ld hl,0             ; zeroise score.
       ld (score),hl
; Initialise coordinates.
       defb 33,21,15       ; load hl pair with starting coords for player.
       ld (plx),hl         ; set player coords.
slevel call dbat           ; remove bat if already on screen.
       call dscor          ; display the score.
       ld a,1              ; lower screen.
       call 5633           ; open channel.
       ld a,22
       rst 16
       ld a,1              ; bottom line of screen.
       rst 16
       ld a,30             ; right edge.
       rst 16
       call splayr         ; show player base symbol.
       call white
       ld a,(lives)
       add a,48            ; ASCII code for zero.
       rst 16              ; display lives as digit.
       call uscrn          ; restore upper screen printing.
       defb 33,255,255     ; default values to switch off player's bullets.
       ld (pbx),hl         ; set bullet coords.
       defb 33,255,255     ; load hl pair with starting coords for bat.
       ld (batx),hl        ; set player coords.
       xor a               ; zeroise accumulator.
       ld (dead),a         ; set flag to say player is alive.
; Initialise segments.
       ld a,8              ; number of segments.
       ld (numseg),a       ; used to establish how many are still on screen.
       ld b,a              ; number of segments to initialise.
       ld hl,segmnt        ; segment table.
segint ld (hl),1           ; start off moving right.
       inc hl
       ld (hl),0           ; start at top.
       inc hl
       ld (hl),b           ; use B register as y coordinate.
       inc hl
       djnz segint         ; repeat until all initialised.
; Now we want to fill the play area with mushrooms.
       ld a,16             ; ASCII code for INK control.
       rst 16
       ld a,4              ; 4 = colour green.
       rst 16              ; we want all mushrooms in this colour!
       ld b,32             ; start with a few.
mushlp ld a,22             ; ASCII code for coordinate control 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,146            ; UDG 'C' is the mushroom graphic.
       rst 16              ; put mushroom on screen.
       djnz mushlp         ; loop back until all mushrooms displayed.
; This is the main game loop.
mloop  equ $
; Delete the player.
       call basexy         ; set the x and y positions of the player.
       call wspace         ; leave white on black behind.
; Now we've deleted the player we can move him before redisplaying him
; at his new coordinates.  Unfortunately in 1k there's no room for fancy
; keyboard/joystick routines so we'll just read a single row of keys.
       ld bc,63486         ; port for keyboard row 1-5 / joystick port 2.
       in a,(c)            ; see what keys are pressed.
       ld b,a              ; remember the value.
       rr b                ; outermost bit = key 1.
       call nc,mpl         ; it's being pressed, move player left.
       rr b                ; next bit along (value 2) = key 2.
       call nc,mpr         ; being pressed, so move player right.
       rr b                ; next bit (value 4) = key 3.
       call nc,mpd         ; being pressed, so move player down.
       rr b                ; next bit (value 8) reads key 4.
       call nc,mpu         ; it's being pressed, move player up.
       rr b                ; next bit (value 16) reads key 5.
       call nc,fire        ; being pressed, so fire bullet.
; Now he's moved we can redisplay the player.
       call basexy         ; set the x and y positions of the player.
       call splayr         ; show player.
; Now for the bullet.  First let's check to see if it's hit anything.
       call bchk
       call dbull          ; delete bullets.
       call moveb          ; move bullets.
       call bchk           ; check new position of bullets.
       call pbull          ; print bullets at new position.
; Time to sort out the vampire bat.
       call batchk         ; hit anything?
       call dbat           ; delete bat.
       call mbat           ; move bat.
       call batchk         ; hit anything at this new position?
       call pbat           ; print bullets at new position.
       call sbat           ; see if we can start the bat.
       ld hl,1152          ; pitch.
       ld de,6             ; duration.
       call 949            ; ROM routine to produce BEEP.
; Now for the centipede segments.
       ld ix,segmnt        ; table of segment data.
       ld b,8              ; number of segments in table.
censeg push bc
       ld a,(ix)           ; is segment switched on?
       inc a               ; 255=off, increments to zero.
       call nz,proseg      ; it's active, process segment.
       pop bc
       ld de,3             ; 3 bytes per segment.
       add ix,de           ; get next segment in ix registers.
       djnz censeg         ; repeat for all segments.
       halt                ; delay.
       ld a,(numseg)       ; number of segments remaining on screen.
       and a               ; same as cp 0 but saves 1 byte.
       jp z,slevel         ; start next level.
       ld a,(dead)         ; was the player killed by a segment?
       and a
       jp nz,killed        ; player killed - lose a life.
; 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.
       ld de,(plx)         ; current coords.
       dec d               ; look 1 square to the left.
       ld (dispx),de       ; set up general-purpose x and y coords.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushroom attributes 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.
       ld de,(plx)         ; current coords.
       inc d               ; look 1 square to the right.
       ld (dispx),de       ; set up general-purpose x and y coords.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushroom attributes 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 12               ; is it half-way up (12)?
       ret z               ; yes - we can go no further then.
       ld de,(plx)         ; current coords.
       dec e               ; look 1 square higher.
       ld (dispx),de       ; set up general-purpose x and y coords.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushroom attributes 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.
       ld de,(plx)         ; current coords.
       inc e               ; look 1 square down.
       ld (dispx),de       ; set up general-purpose x and y coords.
       call atadd          ; get address of attribute at this position.
       cp 68               ; mushroom attributes are bright (64) + green (4).
       ret z               ; there's a mushroom - we can't move there.
       inc (hl)            ; add 1 to x coordinate.
       ret
; Start the vampire bat.
sbat   ld a,(batx)         ; bat vertical coord.
       inc a               ; 255 is default value, increments to zero.
       ret nz              ; bat currently on screen, can't start again.
       call random         ; want random number 0-255.
       cp 32               ; 1 in 256 chance of starting the bat.
       ret nz              ; don't start the bat off.
       call random         ; fetch another random number.
       and 31              ; need it in range 0-31.
       ld (baty),a         ; use this as y coordinate.
       xor a               ; a=0, top of screen.
       ld (batx),a         ; set bat vertical.
       ret
batchk ld a,(batx)         ; bat vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, not on screen.
       ld hl,(batx)        ; bat's coordinates.
       ld a,(pbx)          ; bullet coordinates.
       cp l                ; x match?
       jr nz,batck0        ; no, try player collision instead.
       ld a,(pby)          ; bullet y.
       cp h                ; does it match bat y?
       jr nz,batck0        ; no, check player coords instead.
; So the bat has collided with the bullet.
       call dbull          ; delete bullet.
       call kilbul         ; kill the bullet.
       ld hl,(score)       ; get current score.
       ld de,10            ; increment by 10.
       add hl,de
       ld (score),hl       ; update score.
       call dscor          ; show new points tally on screen.
kilbat ld a,255            ; 255=off.
       ld (batx),a         ; switch bat off.
       ret
batck0 ld a,(ply)          ; bullet y.
       cp h                ; does it match bat y?
       ret nz              ; no collision.
       ld a,(plx)
       cp l                ; x match?
       ret nz              ; no player collision.
; So the bat has collided with the player.
       jp killpl           ; kill the player.
; Fire a missile.
fire   ld a,(pbx)          ; bullet vertical coord.
       inc a               ; 255 is default value, increments to zero.
       ret nz              ; bullet currently on screen, can't fire again.
       ld hl,(plx)         ; player coordinates.
       dec l               ; 1 square higher up.
       ld (pbx),hl         ; set bullet coords.
       ret
bchk   ld a,(pbx)          ; bullet vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       ld hl,(pbx)         ; get coords.
       ld (dispx),hl       ; set up general-purpose fields.
       call atadd          ; find attribute here.
       cp 68               ; mushroom attributes are bright (64) + green (4).
       jr z,hmush          ; hit a mushroom!
       ret
hmush  ld a,22             ; AT control code.
       rst 16
       ld a,(pbx)          ; bullet vertical.
       rst 16
       ld a,(pby)          ; bullet horizontal.
       rst 16
       call wspace         ; set INK colour to white.
       ld hl,(score)       ; get score.
       inc hl              ; add 1 to it.
       ld (score),hl       ; put score back.
       call dscor          ; display score.
kilbul ld a,255            ; x coord of 255 = switch bullet off.
       ld (pbx),a          ; destroy bullet.
       ret
; This routine moves the bullet up the screen 1 character position at a time.
moveb  ld a,(pbx)          ; bullet vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       sub 2               ; 1 row up.
       ld (pbx),a
       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
splayr ld a,16             ; colour control.
       rst 16
       ld a,5              ; colour 5 = cyan.
       rst 16
       ld a,144            ; ASCII code for UDG 'A'.
       rst 16              ; delete player.
       ret
pbull  ld a,(pbx)          ; bullet vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       call bullxy
       ld a,16             ; INK control char.
       rst 16
       ld a,6              ; 6 = yellow.
       rst 16
       ld a,147            ; UDG 'D' is used for player bullets.
       rst 16
       ret
dbull  ld a,(pbx)          ; bullet vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       call bullxy
wspace call white          ; set INK colour to white.
       ld a,32             ; SPACE character.
       rst 16
       ret
white  ld a,16             ; INK control char.
       rst 16
       ld a,6              ; 7 = white.
       rst 16
       ret
; Set up the x and y coordinates for the player's bullet position,
; this routine is called prior to display and deletion of bullets.
bullxy ld a,22             ; AT code.
       rst 16
       ld a,(pbx)          ; player bullet vertical coord.
       rst 16              ; set vertical position of player.
       ld a,(pby)          ; bullet's horizontal position.
       rst 16              ; set the horizontal coord.
       ret
pbat   ld a,(batx)         ; bat vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       call batxy          ; set up bat coords.
       ld a,16             ; INK control char.
       rst 16
       ld a,3              ; 3 = magenta.
       rst 16
       ld a,148            ; UDG 'E' is used for the bat.
       rst 16
       ret
dbat   ld a,(batx)         ; bat vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, no bullet on screen.
       call batxy          ; set up bat coords.
       jp wspace           ; draw a white space.
; Set up the x and y coordinates for the player's bullet position,
; this routine is called prior to display and deletion of bullets.
batxy  ld a,22             ; AT code.
       rst 16
       ld a,(batx)         ; player bullet vertical coord.
       rst 16              ; set vertical position of player.
       ld a,(baty)         ; bullet's horizontal position.
       rst 16              ; set the horizontal coord.
       ret
; This routine moves the bat down the screen 1 character position at a time.
mbat   ld a,(batx)         ; bat vertical.
       inc a               ; is it at 255 (default)?
       ret z               ; yes, it's not on screen.
       cp 22               ; past bottom of screen?
       jp z,kilbat         ; gone past so kill bat off.
       ld (batx),a         ; new bat x coordinate.
       ret
proseg call segcol         ; segment collision detection.
       ld a,(ix)           ; check if segment was switched off
       inc a               ; by collision detection routine.
       ret z               ; it was, so this segment is now dead.
       call segxy
       call wspace         ; leave white on black attributes behind.
       call segmov         ; move segment.
       call segcol         ; new segment position collision check.
       ld a,(ix)           ; check if segment was switched off
       inc a               ; by collision detection routine.
       ret z               ; it was, so this segment is now dead.
       call segxy
       ld a,16             ; INK character code.
       rst 16
       ld a,2              ; 2 = red segment.
       rst 16
       ld a,145            ; UDG 'B' to display segment.
       rst 16
       ret
segmov ld a,(ix+1)         ; x coord.
       ld (dispx),a        ; GP x area.
       ld a,(ix+2)         ; y coord.
       ld (dispy),a        ; GP y area.
       ld a,(ix)
       and a               ; is the segment heading left?
       jr z,segml          ; going left - jump to that bit of code.
; so segment is going right then!
segmr  ld a,(ix+2)         ; y coord.
       cp 31               ; already at right edge of screen?
       jr z,segmd          ; yes - move segment down.
       inc a               ; look right.
       ld (dispy),a        ; set up GP y coord.
       call atadd
       cp 68               ; mushroom attributes are bright (64) + green (4).
       jr z,segmd          ; mushroom to right, move down instead.
       inc (ix+2)          ; no obstacles, so move right.
       ret
; so segment is going left then!
segml  ld a,(ix+2)         ; y coord.
       and a               ; already at left edge of screen?
       jr z,segmd          ; yes - move segment down.
       dec a               ; look right.
       ld (dispy),a        ; set up GP y coord.
       call atadd
       cp 68               ; mushroom attributes are bright (64) + green (4).
       jr z,segmd          ; mushroom to left, move down instead.
       dec (ix+2)          ; no obstacles, so move left.
       ret
; so segment is going down then!
segmd  ld a,(ix)           ; segment direction.
       xor 1               ; reverse it, so left becomes right and vice versa.
       ld (ix),a           ; store new direction.
       ld a,(ix+1)         ; y coord.
       cp 21               ; already at bottom of screen?
       jr z,segmt          ; yes - move segment to the top.
; At this point we're moving down regardless of any mushrooms that may block
; the segment's path.  Anything in the segment's way will be obliterated.
       inc (ix+1)          ; haven't reached the bottom yet, so move down.
       ret
; moving segment to the top of the screen.
segmt  xor a               ; same as ld a,0 but faster and saves 1 byte.
       ld (ix+1),a         ; new x coordinate = top of screen.
       ret
; Segment collision detection.
; Checks for collisions with player and player's bullets.
segcol ld a,(ply)          ; bullet y position.
       cp (ix+2)           ; is it identical to segment y coord?
       jr nz,bulcol        ; y coords differ, try bullet instead.
       ld a,(plx)          ; player x coord.
       cp (ix+1)           ; same as segment?
       jr nz,bulcol        ; x coords differ, try bullet instead.
; So we have a collision with the player.
killpl ld (dead),a         ; set flag to say that player is now dead.
       ret
; Let's check for a collision with the player's bullet.
bulcol ld a,(pbx)          ; bullet x coords.
       inc a               ; at default value?
       ret z               ; yes, no bullet to check for.
       cp (ix+1)           ; is bullet x coord same as segment x coord?
       ret nz              ; no, so no collision.
       ld a,(pby)          ; bullet y position.
       cp (ix+2)           ; is it identical to segment y coord?
       ret nz              ; no - no collision this time.
; So we have a collision with the player's bullet.
       call dbull          ; delete bullet.
       ld a,22             ; AT code.
       rst 16
       ld a,(pbx)          ; player bullet vertical coord.
       inc a               ; 1 line down.
       rst 16              ; set vertical position of mushroom.
       ld a,(pby)          ; bullet's horizontal position.
       rst 16              ; set the horizontal coord.
       ld a,16             ; ASCII code for INK control.
       rst 16
       ld a,4              ; 4 = colour green.
       rst 16              ; we want all mushrooms in this colour!
       ld a,146            ; UDG 'C' is the mushroom graphic.
       rst 16              ; put mushroom on screen.
       call kilbul         ; kill the bullet.
       ld (ix),a           ; kill the segment.
       ld hl,numseg        ; number of segments.
       dec (hl)            ; decrement it.
       ld hl,(score)       ; score.
       ld de,5             ; increment by 5.
       add hl,de
       ld (score),hl       ; new score.
       jp dscor            ; display new score.
; Set up the x and y coordinates for an individual segment's position,
; this routine is called prior to display and deletion of segments.
segxy  ld a,22             ; AT code.
       rst 16
       ld a,(ix+1)         ; segment vertical coord.
       rst 16              ; set vertical position of player.
       ld a,(ix+2)         ; segment horizontal position.
       rst 16              ; set the horizontal coord.
       ret
killed ld hl,lives         ; number of lives.
       dec (hl)            ; decrease by one.
       jp z,restrt         ; none left - game ends so jump to restart point.
       call 3503           ; clear screen.
       jp slevel           ; start level again.
; Score display routine.
dscor  ld a,1              ; lower screen.
       call 5633           ; open channel.
       ld a,22
       rst 16
       ld a,1              ; bottom line of screen.
       rst 16
       xor a               ; zero = left edge.
       rst 16
       ld bc,(score)
       call 6683           ; ROM routine - displays number in BC registers.
uscrn  ld a,2              ; 2 = upper screen.
       jp 5633             ; open channel.
numseg defb 0              ; number of segments.
lives  defb 0              ; number of lives remaining.
score  defw 0              ; score.
dead   defb 0              ; flag - player is dead when this is non-zero.
plx    defb 0              ; player's coordinates.
ply    defb 0
pbx    defb 255            ; player's bullet coordinates.
pby    defb 255
batx   defb 255            ; bat coordinates.
baty   defb 255
dispx  defb 0              ; general purpose coordinates.
dispy  defb 0
; 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,(dispx)        ; 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=22528, address of screen attributes.
       ld d,a
       ld a,e
       and 224
       ld e,a
       ld a,(dispy)        ; horizontal position.
       add a,e
       ld e,a              ; de=address of attributes.
       ld a,(de)           ; return with attribute in accumulator.
       ret
; We'll use some rather naff UDG graphics to save space.
blocks defb 16,16,56,56,124,124,254,254    ; player base.
       defb 24,126,126,255,255,126,126,24  ; segment.
       defb 24,126,255,255,60,60,60,60     ; mushroom.
       defb 0,102,102,102,102,102,102,0,0  ; player's bullets.
       defb 130,198,214,186,214,124,56,16  ; bat.
; Table of segments.
; Format: 3 bytes per entry.
; byte 1: 255=segment off, 0=left, 1=right.
; byte 2 = x (vertical) coordinate.
; byte 3 = y (horizontal) coordinate.
segmnt equ $