11,419 bytes added,
06:54, 5 May 2018 == Overview ==
So, I'm here today presenting a simple game I initially coded in BASIC, with the thought of compiling it through a simple BASIC compiler I came across from a 1985 issue of Happy Computer. But the compiler didn't do random numbers, so I ditched any prospect of using it and considered coding in Assembly instead. Seemed to be the next logical step after all the help I've had from the discussion forum and nagging certain people of certain problems again and again, I've created a simple game out of a BASIC one relying on a lot of firmware to get the job done. The end result I'm quite pleased with, though please do consider improving the code or add as I'm no Assembly boffin!
== BASIC Version ==
I'll start off with a simple game I coded in BASIC, which is a standard run of the mill dodging the hazards game with your space rocket. In this version I've placed the space rocket in the middle of the screen to make the game more challenging, so in order to scroll the screen, the space rocket needs to be deleted and redrawn. The Assembly version is that fast that I've placed the space rocket at the bottom of the screen, which removes the need to delete it, though as everything is rolled off-screen, the space rocket still needs to be redrawn.
<pre>
10 MODE 0:DEFINT a-z:INK 0,11:INK 1,26:BORDER 11:PEN 1
20 RANDOMIZE TIME:RANDOMIZE RND
30 FOR a=1 TO 25
40 x=INT(RND*20)+1
50 LOCATE x,2
60 PRINT CHR$(225)
70 GOSUB 1000:GOSUB 1000
80 NEXT a
90 x=10:y=12:PEN 3:LOCATE x,y:PRINT CHR$(239)
100 d=1
110 WHILE d=1
120 IF INKEY(1)=0 THEN IF x<20 THEN LOCATE x,y:PRINT" ":x=x+1:LOCATE x,y:PEN 3:PRINT CHR$(239)
130 IF INKEY(8)=0 THEN IF x>1 THEN LOCATE x,y:PRINT" ":x=x-1:LOCATE x,y:PEN 3:PRINT CHR$(239)
140 ax=INT(RND*20)+1
150 LOCATE ax,2:PEN 1
160 PRINT CHR$(225)
170 LOCATE x,y:PRINT" "
180 GOSUB 1000
190 IF TEST((x-1)*32+4,238)=1 THEN d=0
200 GOSUB 1000
210 LOCATE x,y:PEN 3:PRINT CHR$(239)
220 CALL &BD19
230 WEND
240 SOUND 1,0,10,15,,,31:LOCATE x,y:PEN 3:PRINT CHR$(238);
250 END
1000 LOCATE 1,1:PRINT CHR$(11)
1010 RETURN
</pre>
== Assembly Version ==
With that BASIC layout in place, I could use that as a guide for coding the Assembly version. Dispite having an assembly routine to produce random numbers, it produces values between 1 and 255 (I think), so the BASIC program easily generates numbers between 1 and 20. I'm able to scale numbers down easily using Assembly instruction to shift the value in 'a' register, though I end up with a value between 1 & 16 and I guess if I had subtracted that random value, it would lead to other complications if the random number was a small one. But adjusting the playing field between 1 & 16 wasn't a problem.
To reflect the speed of Assembly I've enhanced the graphics, originally I was using text characters (like the BASIC version). I then moved to a Sprite Driver which gave me headaches and after making adjustments to screen offsets to work in with the Sprite Driver, the game would eventually crash. I also tried alternative Scrolling Methods, though nothing seemed to be as Fast as SCR HW ROLL. Fortunately a year or so ago I coded a really simple Sprite Driver which takes the patterns of User Defined Graphics and puts them together using Transparent mode, pen colours can be used for the different parts of the graphic & text based coordinates place the image onscreen. I also need to tell the Sprite Driver how many times to Loop as the 3 different sprites used in this game use a different number of characters depending on the colours I've assigned them. As a result I've defined a colour palette, which reuses certain colours for different Pens, though I decided to leave it like that since I'm not running the risk of running out of Pen colours.
<pre>
;; Star Sweeper!
;; Public Domain
;; Please Note:
;; ====== =====
;; This is my 1st attempt of coding an assembly game from a simple
;; BASIC game using Firmware.
;; Program makes use of User Defined Graphics (UDGs), Transparent
;; mode and simple but efficent Text coordinate based Sprite Driver
;; to display 8x8 Multicoloured graphics. SCR HW ROLL handles the
;; -fast- scrolling.
;; Recently I coded a simple Collision Detector using the firmware
;; equivalent of the BASIC TEST() function, which finally had me
;; thinking about the possibility of testing in a simple game.
;; I am by no means an Assembly guru, despite finding it rather
;; interesting to code, which I'm happy to submit onto the CPCWIKI
;; Source Code page. Please feel free to improve.
;; I coded using the Winape Z80 Assembler, so there's a good chance
;; it'll work with MAXAM, DEVPAC users will need to replace '&' with
;; '#' for hexidecimal numbers as well as any full stops prior to
;; label names with a colon after the label name (e.g. .drawobstacles
;; becomes drawobstacles:)
;; Instructions
;; ============
;; Simply dodge the oncomming rocks (bolders) hurdling towards you as
;; you fly through space.
;; Controls are left or right arrow keys.
;; Execute the game with CALL &8000 from BASIC, the game commences
;; immediately (hopefully not in a spot where your rocket is on top
;; of a bolder!).
org &8000
;; Initialize Screen mode, inks & User Defined Graphics
xor a
call &bc0e ;; Mode 0
ld hl,colours
call setinks
ld de,247
ld hl,matrix_table
call &bbab
ld hl,sprites
ld de,matrix_table
ld bc,72
ldir
call genseed ;; Randomize Seed
ld a,1
call &bb90 ;; Pen 1
ld a,1
call &bb9f ;; Transparent Mode on
;; Draw Obstacle Routine
;; The game begins by Printing and scrolling the screen with
;; bolders on it. Once that has looped 24 times, the rocket and
;; main game commence.
ld b,24
.drawobstacles
push bc
call rand
call scalenum
ld a,(result)
ld (xpos1),a
ld a,1
ld (ypos1),a
ld a,250
ld (char),a
ld a,4
ld (col),a
ld b,2
call print_spr
call scroll
call scroll
pop bc
djnz drawobstacles
call printrocket
.maingame
call updaterocket
ld a,(dead)
or a
jr z,skip
ld a,1
call &bb1e ;; KM Test Key
jr z,checkleft
ld a,(xpos)
cp 16
jr z,checkleft
inc a
ld (xpos),a
call printrocket
ld a,(xpos)
ld (ox),a
ld hl,(ex)
ld de,32
add hl,de
ld (ex),hl
.checkleft
ld a,8
call &bb1e ;; KM Test Key
jr z,skip
ld a,(xpos)
cp 1
jr z,skip
dec a
ld (xpos),a
call printrocket
ld a,(xpos)
ld (ox),a
ld hl,(ex)
ld de,32
and a
sbc hl,de
ld (ex),hl
.skip ld a,(dead)
and a
jr nz,maingame
xor a ;; ld a,0
call &bb9f
ld hl,(ypos)
call &bb75
ld a,32
call &bb5a
ld a,1
call &bb9f
ld b,4
ld a,252
ld (char),a
ld a,6
ld (col),a
ld hl,(ypos)
ld (ypos1),hl
call print_spr
call explosion
xor a ;; ld a,0
call &bb9f ;; Transparent mode off
call &bb03 ;; Clear Input (KM RESET)
ld a,(dead)
inc a
ld (dead),a ;; Reset Dead variable
ret ;; Return to BASIC
;; Print Rocket Routine
;; A routine dedicated to printing the rocket onscreeen.
;; The print_spr routine works by using the transparent mode, redefined
;; characters, pen colours & loops to produce a multicoloured 8x8 image.
.printrocket
ld hl,(ypos)
ld (ypos1),hl
ld a,247
ld (char),a
ld a,1
ld (col),a
ld b,3
call print_spr
ret
;; These routines handle the scrolling, collision tests and
;; printing of rocket and bolders.
.updaterocket
call scroll
call collision
call scroll
call printrocket
call &bd19 ;; FRAME (MC WAIT FLYBACK)
;; call updateobstacle
;; ret
.updateobstacle
call rand
call scalenum
ld a,(result)
ld (xpos1),a
ld a,1
ld (ypos1),a
ld a,250
ld (char),a
ld a,4
ld (col),a
ld b,2
call print_spr
ret
;; Collision Detection
;; In order to test if the rocket ship has collided with a bolder
;; I've written a routine which tests for a pixel. Test is a
;; Graphical routine though, so in order to use this, I've setup
;; ex and ey which holds the graphical positions in front and
;; above the rocket. ex had 12 added to it to allow the test to
;; return a non-zero result. When moving left or right with the
;; cursor keys ex is updated by either subtracting 32 or adding
;; 32.
.collision
ld hl,(ex)
ex hl,de
ld hl,(ey)
call &bbf0 ;; TEST(ex,ey)
or a ;; cp 0
jr z,endcoll
ld a,(dead) ;;
dec a ;; You're Dead
ld (dead),a ;;
.endcoll
ret
;; Gen Seed routine
;; Explained below
.genseed
ld a,r
ld (seed),a
ret
;; 8bit Random Number generator
;; It takes a value stored in seed and returns a new number which gets
;; stored back into seed. In order make the routine a bit more random
;; the Gen Seed routine is used to obtain a random value from the
;; refresh register (r).
.rand ld a,(seed)
ld b,a
add a,a
add a,a
add a,b
inc a
ld (seed),a
ret
;; Setinks
;; Entry Conditions:
;; hl = colours
;; This just sets up a colour palette using a loop to increment through
;; the PEN colours, c obtains the contents of hl which is the ink and is
;; passed to b to prevent inks flashing. af & hl are pushed onto the stack
;; because the SCR SET INK firmware alters these registers and pop restores
;; them. When all 15 pens have been done, 'a' register is incremented if
;; this equals 16 then a jr c will not jump as there is no carry.
.setinks
ld c,(hl)
ld b,c
push af
push hl
call &bc32 ;; SCR SET INK
pop hl
pop af
inc hl
inc a
cp 16
jr c,setinks
ret
;; Scroll Routine
;; a = 0 to return a black background
;; b = 0 to roll the screen top to bottom
;; to make this game a little bit more challenging, I'm using SCR HW ROLL
;; which rolls the screen very quickly.
;; To space out the bolders, I've called this routine twice, which has
;; produced this intense game, but it's always going to produce quite a
;; bit of flicker.
.scroll
xor a
ld b,a
call &bc4d ;; SCR HW ROLL
ret
;; Scale number routine
;; This takes the number produced by the random number generator which is
;; in the range between 1 & 255 I think, srl divides the number by 2 and
;; is done 4 times to return a number between 1 and 16.
.scalenum
ld a,(seed)
srl a
srl a
srl a
srl a
inc a
ld (result),a
ret
;; Print Sprite routine
;; Entry Conditions:
;; B = Number of times to Loop (3, 2 or 4)
;; Char = Sprite number (247 = Rocket, 250 = Bolder, 252 = Explosion)
;; Col = Sprite Pen Colours (1 = Rocket, 4 = Bolder, 6 = Explosion)
;; Xpos1 & Ypos1 = Positions of Sprites
.print_spr
ld a,(col)
call &bb90
ld hl,(ypos1)
call &bb75
ld a,(char)
call &bb5a
ld a,(col)
inc a
ld (col),a
ld a,(char)
inc a
ld (char),a
djnz print_spr
ret
.explosion
ld hl,snddata
call &bcaa ;; SND Queue
ret
.ypos defb 25
.xpos defb 10
.ypos1 defb 1
.xpos1 defb 5
.ox defb 10
.dead defb 1
.seed defb 0
.result defb 0
.ex defw 300 ;; Test Pixel Colour 4x3 of xpos position, so xpos-1 x 32 = 288
.ey defw 30
.char defb 0
.col defb 0
.snddata
defb 1,0,0,0,0,31,15,10,0
.colours
defb 0,13,26,6,15,3,3,6
defb 24,26,0,0,0,0,0,0
.matrix_table
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
defb 0,0,0,0,0,0,0,0
;; Sprite data
.sprites
defb 24,36,90,90,36,102,0,0
defb 0,24,36,36,24,24,0,0 ;; Rocket
defb 0,0,0,0,0,0,90,0
defb 28,34,65,129,129,65,34,28 ;; Bolder
defb 0,28,62,126,126,62,28,0
defb 24,36,66,66,66,36,24,0
defb 0,24,36,36,36,24,0,0 ;; Explosion
defb 0,0,8,24,24,0,0,0
defb 0,0,16,0,0,0,0,0
.sprites_end
defb 0
</pre>
== Screenshots ==