Programming:Coding a simple BASIC game into Assembly

From CPCWiki - THE Amstrad CPC encyclopedia!
Jump to: navigation, search

Overview

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!

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.

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

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.

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

Screenshots

--CPM User (talk) 02:55, 5 May 2018 (EDT)