Programming:Coding a simple BASIC game into Assembly
Initially I created a simple game in BASIC, with the thought of compiling it using a simple BASIC compiler that I came across from a 1985 issue of Happy Computer, though there were a few issues that I couldn’t resolve with it, such as the 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!
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.
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
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.
;; 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