|
|
| 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 $
|
 |
|  |
|
|
|
|