My new game - CSSCGC 2018 entry

People are still making stuff for the Sinclair related machines. Tell us about new games and other software that runs on the Spectrum, ZX80/ZX81, Pentagon and Next.
User avatar
MonkZy
Dizzy
Posts: 89
Joined: Thu Feb 08, 2018 1:01 pm

My new game - CSSCGC 2018 entry

Post by MonkZy » Wed Mar 07, 2018 1:43 pm

I have made I game for the CSSCGC, it is called go race!

Keys are :
D - Mode
S - Start Game
ZX - Left and Right

I am new to z80 assembly so if I have inadvertently discovered a way you can fry rare silicon components with z80 instructions, I am truly sorry for your losses. Use at own risk. Feel free to distribute, post screenshots, disassemble, shovel on a cassette or CD, or burn with fire.

Here are the files, tested OK on FUSE (I think):
gorace.tap
gorace.tzx

and the huge and uncompressed .wav version to play into the ear of any nearby spectrum (maybe not 16kb)..

gorace.wav

Assembly for laffs :
SpoilerShow

Code: Select all


	; GO RACE! by MonkZy, written for the CSSCGC 2018

	;###Initialisation###

	org 60000



	ld hl,lcd_chars		;load HL with address of LCD chars
	ld (23675),hl		;change UDG system variable to point to LCD chars

	ld a,120		;Set the location of the character set
	ld (23606),a
	ld a,229
	ld (23607),a

        ld a,7			;BORDER 7
	call 8859
	ld a,56			;INK 0: PAPER 7
	ld (23693),a
	call 3503		;CLS

	ld a,2                  ;channel 2 = "S" for screen
	call $1601              ;select print channel using ROM

	;###Routine to print a page of intro text###
	
	ld b,18			;load A with number of lines of intro text
	ld hl,introtext		;load HL with start location of intro text
textloop:
	push bc			;preserve B for loop

	ld d,h        		;load DE with the address of the printline
	ld e,l
	ld bc,35		;length of a line + AT codes
	push bc
	call 8252		;print the line (destroys BC)
	pop bc			;reload BC with line length
	add hl,bc		;add length of printline to address in HL
	pop bc			;restore B for loop
	djnz textloop		;b-=1, loop until b=0

	ld hl,23560         	;LAST K system variable.
	ld (hl),0           	;put null value there.
loopy:				;endless loop waits for keypress (Jonathan Cauldwell)
	ld a,(hl)           	;new value of LAST K.
	cp 0                	;is it still zero?
	jr z,loopy           	;yes, so no key pressed.

	;###Copy LCD game image from RAM to display###
	
	ld a,63			;INK 7: PAPER 7
	ld (23693),a
	call 3503		;CLS
	halt
	ld de,16384		;load DE with address of screen memory
	ld hl,40000		;load HL with address of stored image
	ld bc,6912		;copy 6912 bytes
	ldir			;copy image to screen


	;###Acky Kacky startup flicker
	
	ld b,80
pause1:	
	halt
	djnz pause1
	
	ld a,56
	call pokescreen
	
       	ld b,12
pause2:	
	halt
	djnz pause2
	
	ld a,63
	call pokescreen
	
	ld b,20
pause3:	
	halt
	djnz pause3
	
	ld a,56
	call pokescreen

	ld de,5
	ld hl,900
	call 949

	ld b,80	
pause4:	
	halt
	djnz pause4
	
	;###initialisation for the demo loop###
	
predemo:
	ld a,63
	call pokescreen

	ld hl,22680
	ld (hl),56
	ld hl,22648
	ld b,3
	ld c,0
game_cell_loop:
	ld (hl),56
	inc hl
	djnz game_cell_loop

	;###Demo Loop - shows hiscore and a rolling road###
	
demo:	
	ld b,40
delayloopD:
	halt
	push bc
	ld b,255                ;ld B with 255
	
keyloopD:
	push bc			;preserve B for loop
	ld bc,65022         	;keyboard row v,c,x,z,caps
	in a,(c)            	;see what keys are pressed.
lastkeyD:	
	cp 191		      	;compare new keypress with last keypress (modified with code)
	jp z,skipinputD		;jump out if the keypress is the same as the last
	ld (lastkeyD+1),a	;modify cp line for new keypress
			
	cp 189			;compare with 's'
	jp nz,not_s		;if keypress is not 's' move to next pre-game key check
	jp pregame
	
not_s:	
	cp 187			;compare with 'd'
	jp nz,skipinputD	;if keypress is not 'd' jump out
	ld a,(mode)
	ld hl,22680
	ld b,0
	ld c,a
	add hl,bc
	ld (hl),63
	xor 1
	ld (mode),a
	ld hl,22680
	ld b,0
	ld c,a
	add hl,bc
	ld (hl),56	
skipinputD:	
	pop bc
	djnz keyloopD		;b-=1, loop until b=0
	
	pop bc
	djnz delayloopD		;b-=1, loop until b=0
	
	call advanceroad
	ld hl,(hiscore)
	call printscore
	jp demo

	;###Routine to copy a single attribute to all of the LCD display window###
	;A=8bit attribute data
	
pokescreen:
	ld d,a		  	;put attribute data into D
	ld hl,22629		;load HL with first attribute address (dest)
	ld b,19			;load B with number of lines to copy
loop1:
	push bc			;preserve B for loop1
	ld b,22			;load B with number of bytes in one line
loop2:
	ld a,(hl)		;retrieve current attribute
	cp 60			;compare with INK 4; PAPER 0 (This preserves the green 'silk-screen' areas)
	jp z,skippoke		;if true then skip next POKE
	ld (hl),d		;load next attribute address with A
skippoke:	
	inc hl			;increment attribute address
	djnz loop2		;B-=1 loop until B==0
	ld bc,10		;Add 10 to the attribute address 
	add hl,bc		;(32-bytes in one line)
	pop bc			;restore B for loop1
	djnz loop1		;B-=1 loop until B==0
	ret

	;###Pre-game initialisation###
	
pregame:
	ld a,(mode)
	cp 1
	jp z,gameb
gamea:	
	ld a,40
	ld (speed),a
	ld a,50
	ld (sp_up+1),a
	ld a,5
	ld (sp_modify+1),a
	ld a,25
	ld (sp_up_modify+1),a
	ld a,20
	ld (sp_max+1),a
	jp set_vars
gameb:
	ld a,30
	ld (speed),a
	ld a,25
	ld (sp_up+1),a
	ld a,3
	ld (sp_modify+1),a
	ld a,15
	ld (sp_up_modify+1),a
	ld a,18
	ld (sp_max+1),a
set_vars:	
	ld hl,0
	ld (score),hl
	ld a,3
	ld (lives),a

	ld hl,22634
	ld de,22666
	ld b,10
	ld c,0
	ld a,56
HUD_cell_loop:
	ld (hl),a
	ld (de),a
	inc hl
	inc de
	djnz HUD_cell_loop

resetroad:

	ld b,4
	ld a,56
fuel_cell_loop:	
	ld (de),a
	inc de
	djnz fuel_cell_loop

	ld a,0
	ld (fuel_deployed),a
	ld (fuelcounter),a
	ld a,4
	ld (fuel),a
	
	ld a,63
	ld b,29
	ld hl,fueldata
resetloop:	
	ld (hl),a
	inc hl
	djnz resetloop		;b-=1, loop until b=0
	call advanceroad
	ld hl,(score)
	call printscore
	ld a,(carpos)
	ld c,56
	ld hl,23177
	call setaddr
	call setblock
main:
	ld a,(speed)
	ld b,a
delayloop:
	halt
	push bc
	ld b,0
keyloop:
	push bc			;preserve B for loop
	ld bc,65278         	;keyboard row v,c,x,z,caps
	in a,(c)            	;see what keys are pressed.
lastkey:	
	cp 191		      	;compare new keypress with last keypress (modified with code)
	jp z,skipinput		;jump out if the keypress is the same as the last
	ld (lastkey+1),a	;modify cp line for new keypress		
	cp 189			;compare with 'z'
	jp nz,not_z		;if keypress is not 'z' move to next compare
	ld a,(carpos)		;load A with carpos (1-3)
	cp 1
	jp z,skipinput		;carpos is 1, jump out
	call clear_car
	dec a			;decrease carpos
	jp set_new_pos	
not_z:
	cp 187			;compare with 'x'
	jp nz,skipinput		;if keypress is not 'x' jump out
	ld a,(carpos)		;load A with carpos (1-3)
	cp 3
	jp z,fuel_test		;carpos is 3, test for fuel
	call clear_car
	inc a			;increase carpos
	jp set_new_pos

fuel_test:
	ld a,(fueldata)
	cp 56
	jp nz,skipinput

	ld hl,22676
	ld b,4
refill_fuel:	
	ld (hl),a
	inc hl
	djnz refill_fuel
	inc a
	ld (fueldata),a
	ld a,4
	ld (fuel),a
	call biiip
	jp skipinput
	
set_new_pos:	
	ld (carpos),a
	ld c,56			;load C with a INK 0;PAPER 7 attributes
	ld hl,23177
	call setaddr
	call setblock

skipinput:	
	pop bc
	djnz keyloop		;b-=1, loop until b=0
	
	pop bc
	djnz delayloop		;b-=1, loop until b=0

	

	ld a,(carpos)		;###collision detection###
	ld hl,23081		;set HL to 1st traffic attribute
	call setaddr		;set HL based on carpos
	ld a,(hl)		;get the attribute
	cp 56			;compare attribute to INK 7; PAPER 0
	ld d,0			;set the no-fuel flag to 0 as this is collision
	jp z,collision_detected	;jump to collision routine if true (a=0)

	ld a,(fuel)
	cp 0
	jp z,no_fuel

	call biiip
	
	call advanceroad	;advance the road
	
	ld hl,(score)		;Load HL with score, increment, store
	inc hl
	ld (score),hl
	call printscore		;print the score
	
	ld a,(speedcounter)	;manage the speed increase 
	inc a			;a counter counts
sp_up:	
	cp 55			;check if this number is in A (this is the count at which the speed is increased)
	jp nz,skipcalc		;if it not we will skip the next code
	
	ld a,(speed)		;load A with the speed
sp_max:	
	cp 20			;check for max speed
	jp z,zero_counter	;if we are already at max speed skip to zero_counter
sp_modify:	
	sub 5			;increase the speed (we reduce the amount of HALTS in the delay loop)
	ld (speed),a		;reload the speed into memory
	ld hl,22643		;load hl with the first attribute location for the speed guage
seek_blank:
	inc hl			;this loop will set HL to the first blank space in the speed guage
	ld a,(hl)
	cp 63			;look for INK 7:PAPER 7
	jp nz,seek_blank	;loop if not I7 P7
	ld a,56			;set the loaction INK 0: PAPER 7
	ld (hl),a
	
	ld a,(sp_up+1)		;modify code to increase number of advances until a speed increase
sp_up_modify:	
	add a,50
	ld (sp_up+1),a
	
	
zero_counter:
	ld a,0			;zero the speed counter
skipcalc:
	ld (speedcounter),a	;store the speedcounter into memory

	jp main

no_fuel:
	ld d,1

collision_detected:
	push de
	call datoon		;initialise the death tune
	
	ld hl,23113		;set HL to the first attribute address of the explosion
	ld a,(carpos)
	ld c,56			;set C to PAPER 7;INK 0
	call setaddr
	pop de
	ld b,10			;load B with the number of notes in the death tune
toonloop:
	push bc			;preserve HL,BC and DE registers
	push hl
	push de
	ld a,d
	cp 1
	jp nz,no_fuel_flash
	ld a,c
	ld (22675),a
no_fuel_flash:
	call setblock		;set the explosion block of attributes
	pop de
	pop hl
	pop bc			;restore registers
	push hl			;preserve HL and BC
	push bc
	push de
	call NEXTNOTE		;play the next note of the death tune
	pop de
	pop bc			;restore registers
	pop hl
	ld a,c			;XOR 7 the attribute bits to flash beetween INK 0 and INK 7
	xor 7
	ld c,a
	djnz toonloop		;loop until B=0

	ld a,(lives)
	ld hl,22639
	ld de,22671
	ld b,3
seek_life:	
	cp b
	jp z,remove_life
	dec b
	dec hl
	dec hl
	dec de
	dec de
	jp seek_life
remove_life:
	ld a,63
	ld (hl),a
	ld (de),a
	inc de
	inc hl
	ld (hl),a
	ld (de),a
	ld a,(lives)
	dec a
	cp 0
	jp z,total_death

	ld (lives),a
	
	ld de,22675

	jp resetroad		;jump to resetroad (clear the traffic and restart game)

total_death:
	ld b,8
biploop:
	push bc
	ld b,10
pauseloop:
	halt
	djnz pauseloop
	call biiip
	pop bc
	djnz biploop
	or a
	ld de,(score)
	ld hl,(hiscore)
	sbc hl,de
	jr nc,not_hiscore
	
	ld (hiscore),de
	
not_hiscore:
	jp predemo

biiip:	
	ld de,5			;Make a very short 'Biip' 
	ld hl,900		;an arbitary frequency
	call 949 		;call the BEEP'er
	ret

clear_car:
	ld c,63			;load C with INK 7;PAPER 7 attributes
	ld hl,23177
	push af
	call setaddr
	call setblock		;clear current car
	pop af
	ret

	;setaddr is used to return an address for the first attribute of a car or explosion
	;HL=start address of column 1, 23177=cars 23113=explosion 
	;A=column (1-3)

setaddr:			
	ld d,0			;Load DE with the column offset (5)
	ld e,5
multiply:
	cp 1			;check for column 1
	jp z,exitmult		;if column 1 leave the multiply loop
	add hl,de		;add the offset
	dec a
	jp multiply		;loop
exitmult:
	ret
	
	;setblock is used for changing attributes of the car and explosion
	;changes a 4x2 block of attributes
	;HL=start address,C=8bit Attribute
	
setblock:	
	ld d,h			;load DE with start address
	ld e,l
	ld h,0			;tab load HL with the offset to line 2 (32)
	ld l,32
	add hl,de		;load HL with the start address of line 2
	ld b,4			;load B to count 4 attribute locations
	ld a,c			;load A with the attribute data
setloop:	
	ld (hl),a		;set attribute for line 2 location
	ld (de),a		;set attribute for line 1 location
	inc hl			;increment addresses
	inc de
	djnz setloop		;b-=1, loop until b=0
	ret
	
advanceroad:
	ld a,(fuelcounter)
	inc a
	cp 30
	jp nz,fuel_ok
	
	ld a,(fuel)
	dec a
	ld (fuel),a
	
	ld hl,22676
	ld b,0
	ld c,a
	add hl,bc
	ld (hl),63

	ld a,0
fuel_ok:
	
	ld (fuelcounter),a
	
	ld de,roaddata_row1	;load DE with address of 1st row of roaddata
	ld hl,roaddata_row2	;load HL with address of 2nd row
	ld bc,5			;copy 5 bytes
	ldir			;move road data forward one row

	ld de,trafficdata_row1	;load DE with address of 1st row of roaddata
	ld hl,trafficdata_row2	;load HL with address of 2nd row
	ld bc,15		;copy 15 bytes
	ldir			;move traffic data forward one row

	ld de,fueldata		;load DE with address of 1st row of fueldata
	ld hl,fueldata+1	;load HL with address of 2nd row
	ld bc,5			;copy 5 bytes
	ldir			;move fuel data forward one row

rndpointer:
	ld a,1			;load A with the RND pointer (self modifies)
	ld c,a			;load BC with pointer
	ld b,0
	ld hl,randomness	;load HL with adress of the start of random numbers
	add hl,bc		;add BC to HL to find next random number address
	inc a			;increment the random number pointer
	ld (rndpointer+1),a	;modify code for the new pointer value
	ld a,(hl)		;load A with the random number (0-7)

	ld de,trafficdata_row6	;load DE with address of 6th row
	ld hl,trafficstyle	;load HL with start address of the traffic styles
	ld c,a	
	ld b,0
	add hl,bc		;add the random number to the road style address
	ld bc,3			;copy 3 bytes
	ldir

	and %00000011	        ;strip to two bytes (random 0-3)

	ld de,roaddata_row4	;load DE with address of 4th row
	ld hl,roadstyle		;load HL with start address of the road styles
	ld c,a			;load BC with the random number in A
	ld b,0			
	add hl,bc		;add the random number to the road style address
	ld bc,2			;copy 2 bytes
	ldir


	ld b,a
	ld a,(fuel_deployed)		;add random number to fueling flag
	add a,b			
	cp 0			;chance of fueling
	ld a,63
	ld (fueldata+5),a
	
	jp nz,skip_fuel
	ld a,(trafficdata_row1+14)
	ld b,a
	ld a,(trafficdata_row6+2)
	add a,b
	cp 126
	jp nz,skip_fuel
	ld a,56
	ld (fueldata+5),a
	ld a,1
	ld (fuel_deployed),a
skip_fuel:	
	
	; Directly set attributes for the road, code produced with BASIC tool

	ld a,(roaddata_row1+0) ;1 1
	ld (23111),a
	ld (23143),a
	ld (23175),a
	ld (23207),a

	ld a,(roaddata_row1+1) ;1 2
	ld (23130),a
	ld (23162),a

	ld a,(roaddata_row1+2) ;2 1
	ld d,a
	ld e,a
	ld (22919),de
	ld (22951),de
	ld (22983),de
	ld (23015),a
	ld (23047),a
	ld (23079),a

	ld a,(roaddata_row1+3) ;2 2
	ld d,a
	ld e,a
	ld (22936),a
	ld (22968),a
	ld (23000),a
	ld (23032),de
	ld (23065),a
	ld (23097),de

	ld a,(roaddata_row1+4) ;3 1
	ld d,a
	ld e,a
	ld (22794),de
	ld (22825),de
	ld (22856),de
	ld (22888),de

	ld a,(roaddata_row1+5) ;3 2
	ld d,a
	ld e,a
	ld (22808),de
	ld (22840),de
	ld (22872),a
	ld (22904),a

	ld a,(roaddata_row1+6) ;4 1
	ld d,a
	ld e,a
	ld (22702),de
	ld (22732),de
	ld (22734),a
	ld (22763),de
	ld (22765),a

	ld a,(roaddata_row1+7) ;4 2
	ld d,a
	ld e,a
	ld (22714),a
	ld (22745),de
	ld (22777),de

	ld a,(trafficdata_row1+0) ;1 1
	ld d,a
	ld e,a
	ld (22985),de
	ld (22987),de
	ld (23017),de
	ld (23019),de
	ld (23049),de
	ld (23051),de
	ld (23081),de
	ld (23083),de

	ld a,(trafficdata_row1+1) ;1 2
	ld d,a
	ld e,a
	ld (22990),de
	ld (22992),de
	ld (23022),de
	ld (23024),de
	ld (23054),de
	ld (23056),de
	ld (23086),de
	ld (23088),de

	ld a,(trafficdata_row1+2) ;1 3
	ld d,a
	ld e,a
	ld (22995),de
	ld (22997),de
	ld (23027),de
	ld (23029),de
	ld (23059),de
	ld (23061),de
	ld (23091),de
	ld (23093),de

	ld a,(trafficdata_row1+3) ;2 1
	ld d,a
	ld e,a
	ld (22890),de
	ld (22892),a
	ld (22922),de
	ld (22924),a
	ld (22954),de
	ld (22956),a

	ld a,(trafficdata_row1+4) ;2 2
	ld d,a
	ld e,a
	ld (22895),de
	ld (22897),a
	ld (22927),de
	ld (22929),a
	ld (22959),de
	ld (22961),a

	ld a,(trafficdata_row1+5) ;2 3
	ld d,a
	ld e,a
	ld (22900),de
	ld (22902),a
	ld (22932),de
	ld (22934),a
	ld (22964),de
	ld (22966),a

	ld a,(trafficdata_row1+6) ;3 1
	ld d,a
	ld e,a

	ld (22796),de
	ld (22798),a
	ld (22828),de
	ld (22830),a
	ld (22860),de
	ld (22862),a
	
	ld a,(trafficdata_row1+7) ;3 2
	ld d,a
	ld e,a
	ld (22800),de
	ld (22802),a
	ld (22832),de
	ld (22834),a
	ld (22864),de
	ld (22866),a

	ld a,(trafficdata_row1+8) ;3 3
	ld d,a
	ld e,a
	ld (22804),de
	ld (22806),a
	ld (22836),de
	ld (22838),a
	ld (22868),de
	ld (22870),a

	ld a,(trafficdata_row1+9) ;4 1
	ld d,a
	ld e,a
	ld (22736),de
	ld (22768),de

	ld a,(trafficdata_row1+10) ;4 2
	ld d,a
	ld e,a
	ld (22739),de
	ld (22771),de

	ld a,(trafficdata_row1+11) ;4 3
	ld d,a
	ld e,a	
	ld (22742),de
	ld (22774),de	

	ld a,(trafficdata_row1+12) ;5 1
	ld d,a
	ld e,a
	ld (22705),de

	ld a,(trafficdata_row1+13) ;5 2
	ld d,a
	ld e,a
	ld (22708),de

	ld a,(trafficdata_row1+14) ;5 3
	ld d,a
	ld e,a
	ld (22711),de
	
	ld a,(fueldata) ;car row
	ld d,a
	ld e,a

	ld (23159),de
	ld (23161),a
	ld (23191),de
	ld (23193),a
	ld (23223),de
	ld (23225),a
	
	ld a,(fueldata+1) ;1 
	ld d,a
	ld e,a

	ld (23063),de
	ld (23095),de
	
	cp 56
	jp nz,skip_reset
	ld a,0
	ld (fuel_deployed),a
skip_reset:
	ld a,(fueldata+2) ;2 
	
	ld (22935),a
	ld (22967),a

	ld a,(fueldata+3) ;3 

	ld (22839),a
	ld (22871),a

	ld a,(fueldata+4) ;4 

	ld (22744),a
	ld (22776),a
	
	ld a,(fueldata+5) ;5

	ld (22713),a
	
	
	ret
	
printscore:	
	ld a,143		;change character offset in stripHL for upper LCD chars
	ld (Num1+1),a
	push hl
	call stripHL		;call the digit stripper
	ld a,3			;set Y in printline to 3
	ld (printline+1),a
	ld a,6			;set X in printline to 6
	ld (printline+2),a
	ld de,printline        	;load the address of the printline
	ld bc,10		;length of score + ctrl. codes
	call 8252		;print upper part of score LCD
	
	pop hl
	ld a,153		;change character offset in stripHL for lower LCD chars
	ld (Num1+1),a
	call stripHL		;call the digit stripper
	ld a,4			;set Y in printline to 4
	ld (printline+1),a
	ld de,printline       	;load the address of scoreline line 2the printline
	ld bc,10		;length of score + ctrl. codes
	call 8252           	;print lower part of score LCD
	ret

stripHL:
	ld de,printline+5	;load 'printline' address (jump over the five ctrl. codes)
	ld bc,-10000		;load bc with negative number
	call Num1
	ld bc,-1000		;each call will count and strip a single digit
	call Num1
	ld bc,-100
	call Num1
	ld c,-10
	call Num1
	ld c,-1
Num1:
	ld a,143		;set A with offset for character code for 0, CHR$144 (udg's)
Num2:
	inc a			;count using A
	add hl,bc		;add the negative from BC
	jr c,Num2		;count until carry flag is set 
	sbc hl,bc		;recover HL

	ld (de),a		;store stripped digit into printline

	inc de			;increment DE
	ret			;return (strip next digit OR exit strip routine)

carpos:
	defb 2			;players car position 1 - 3
	
score:
	defb 0,0

padding:
	defb 255,0,255,0,255,0
	
speed:
	defb 40

speedcounter:
	defb 0

lives:
	defb 3
	
fuel:
	defb 4

fuel_deployed:
	defb 0

fuelcounter:
	defb 0

hiscore:
	defb 0,0

mode:
	defb 0
	
printline:
	defb 22,3,6,16,0,38,38,38,38,38

introtext:
	defb 22,1,0,16,2,'           g',16,4,'o ',16,2,'r',16,4,'ace!   ',16,0
	defb 22,4,0,'The year is 1986, you are in the'
	defb 22,5,0,'newsagent. In your pocket is the'
	defb 22,6,0,'tenner your grandma gave you for'
	defb 22,7,0,'xmas. You intend to blow the lot'
	defb 22,8,0,'on three ',96,'2.99 games and still  '
	defb 22,9,0,'have change for a Marathon and a'
	defb 22,10,0,'packet of Salt',39,'n',39,'Shake. All of a'
	defb 22,11,0,'sudden a shiny box catches your '
	defb 22,12,0,'eye, a pocket sized LCD game.   '
	defb 22,13,0,'The idea of true portable gaming' 
	defb 22,14,0,'fills your mind. A game you can '
	defb 22,15,0,'play on the bus! Seeing the cost' 
	defb 22,16,0,'is ',96,'6.99 you are blown away. How' 
	defb 22,17,0,'can this be so? On the bus you  '
	defb 22,18,0,'unwrap your new toy and insert  '
	defb 22,19,0,'the batteries (included)....... '
	defb 22,21,0,'                 press a key..  '
cardata:
	defb 63,56,63

roadstyle:
	defb 56,56,63,56,56

trafficstyle:
	defb 56,56,63,56,63,63,56,56,63,56
	
fueldata:
	defb 63,56,63,56,56,63

roaddata_row1:
	defb 63,63
roaddata_row2:
	defb 63,63,63,63
roaddata_row4:
	defb 63,63

trafficdata_row1:
	defb 63,63,63
trafficdata_row2:
	defb 63,63,63,63,63,63,63,63,63,63,63,63
trafficdata_row6:  
	defb 63,63,63

randomness:
	defb 4, 2, 7, 3, 0, 6, 1, 5
	defb 2, 6, 0, 7, 5, 1, 4, 3
	defb 4, 1, 3, 6, 2, 0, 5, 7
	defb 2, 4, 0, 5, 6, 1, 3, 7
	defb 0, 6, 7, 2, 3, 1, 4, 5
	defb 1, 7, 5, 6, 2, 3, 0, 4
	defb 6, 3, 5, 7, 4, 0, 2, 1
	defb 6, 2, 3, 7, 5, 4, 1, 0
	defb 3, 4, 1, 0, 7, 2, 6, 5
	defb 1, 5, 0, 4, 3, 2, 6, 7
	defb 1, 7, 4, 6, 0, 2, 5, 3
	defb 4, 5, 1, 7, 0, 3, 2, 6
	defb 4, 6, 2, 0, 5, 3, 7, 1
	defb 6, 4, 3, 2, 5, 1, 7, 0
	defb 7, 5, 2, 3, 0, 1, 6, 4
	defb 1, 6, 4, 7, 5, 3, 0, 2
	defb 1, 6, 7, 5, 2, 0, 4, 3
	defb 6, 0, 1, 7, 5, 3, 2, 4
	defb 7, 1, 2, 5, 6, 4, 3, 0
	defb 2, 3, 1, 0, 6, 7, 5, 4
	defb 3, 1, 2, 5, 4, 0, 7, 6
	defb 1, 5, 3, 7, 6, 2, 0, 4
	defb 7, 5, 3, 4, 6, 0, 1, 2
	defb 1, 7, 6, 5, 4, 0, 3, 2
	defb 2, 3, 0, 1, 6, 4, 5, 7
	defb 5, 4, 0, 3, 1, 2, 7, 6
	defb 3, 2, 7, 1, 5, 6, 0, 4
	defb 3, 1, 0, 5, 6, 4, 2, 7
	defb 4, 5, 6, 1, 3, 7, 0, 2
	defb 7, 2, 5, 6, 0, 3, 4, 1
	defb 2, 0, 1, 6, 5, 7, 3, 4
	defb 3, 2, 5, 0, 6, 7, 1, 4

lcd_chars:
	defb %00000000
	defb %00001110	; LCD 0 TOP
	defb %00010001
	defb %00010001
	defb %00010001
	defb %00100010
	defb %00100010
	defb %00100010
	
	defb %00000000
	defb %00000000	; LCD 1 TOP
	defb %00000001
	defb %00000001
	defb %00000001
	defb %00000010
	defb %00000010
	defb %00000010

	defb %00000000
	defb %00001110	; LCD 2 TOP
	defb %00000001
	defb %00000001
	defb %00000001
	defb %00000010
	defb %00000010
	defb %00000010

	defb %00000000
	defb %00001110	; LCD 3 TOP
	defb %00000001
	defb %00000001
	defb %00000001
	defb %00000010
	defb %00000010
	defb %00000010

	defb %00000000
	defb %00000000	; LCD 4 TOP
	defb %00010001
	defb %00010001
	defb %00010001
	defb %00100010
	defb %00100010
	defb %00100010

	defb %00000000
	defb %00001110	; LCD 5 TOP
	defb %00010000
	defb %00010000
	defb %00010000
	defb %00100000
	defb %00100000
	defb %00100000

	defb %00000000
	defb %00000000	; LCD 6 TOP
	defb %00010000
	defb %00010000
	defb %00010000
	defb %00100000
	defb %00100000
	defb %00100000

	defb %00000000
	defb %00001110	; LCD 7 TOP
	defb %00000001
	defb %00000001
	defb %00000001
	defb %00000010
	defb %00000010
	defb %00000010
	
	defb %00000000
	defb %00001110	; LCD 8 TOP
	defb %00010001
	defb %00010001
	defb %00010001
	defb %00100010
	defb %00100010
	defb %00100010
	
	defb %00000000
	defb %00001110	; LCD 9 TOP
	defb %00010001
	defb %00010001
	defb %00010001
	defb %00100010
	defb %00100010
	defb %00100010
	
	defb %00000000
	defb %00100010	; LCD 0 BOTTOM
	defb %00100010
	defb %00100010
	defb %01000100
	defb %01000100
	defb %01000100
	defb %00111000
	
	defb %00000000
	defb %00000010	; LCD 1 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00000000

	defb %00011100
	defb %00100000	; LCD 2 BOTTOM
	defb %00100000
	defb %00100000
	defb %01000000
	defb %01000000
	defb %01000000
	defb %00111000

	defb %00011100
	defb %00000010	; LCD 3 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00111000

	defb %00011100
	defb %00000010	; LCD 4 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00000000

	defb %00011100
	defb %00000010	; LCD 5 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00111000

	defb %00011100
	defb %00100010	; LCD 6 BOTTOM
	defb %00100010
	defb %00100010
	defb %01000100
	defb %01000100
	defb %01000100
	defb %00111000

	defb %00000000
	defb %00000010	; LCD 7 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00000000

	defb %00011100
	defb %00100010	; LCD 8 BOTTOM
	defb %00100010
	defb %00100010
	defb %01000100
	defb %01000100
	defb %01000100
	defb %00111000

	defb %00011100
	defb %00000010	; LCD 9 BOTTOM
	defb %00000010
	defb %00000010
	defb %00000100
	defb %00000100
	defb %00000100
	defb %00000000

                
; *****************************************************************************
; * The Music Box Player Engine
; *
; * Based on code written by Mark Alexander for the utility, The Music Box.
; * Modified by Chris Cowley
; *
; * Produced by Beepola v1.08.01
; ******************************************************************************
 
datoon:
                          LD    HL,MUSICDATA         ;  <- Pointer to Music Data. Change
                                                     ;     this to play a different song
                          LD   A,(HL)                         ; Get the loop start pointer
                          LD   (PATTERN_LOOP_BEGIN),A
                          INC  HL
                          LD   A,(HL)                         ; Get the song end pointer
                          LD   (PATTERN_LOOP_END),A
                          INC  HL
                          LD   (PATTERNDATA1),HL
                          LD   (PATTERNDATA2),HL
                          LD   A,254
                          LD   (PATTERN_PTR),A                ; Set the pattern pointer to zero
                          DI
                          CALL  NEXT_PATTERN
                          EI
                          RET
NEXTNOTE:
                          DI
                          CALL  PLAYNOTE

                          EI
                          RET                                 ; Return from playing tune

PATTERN_PTR:              DEFB 0
NOTE_PTR:                 DEFB 0


; ********************************************************************************************************
; * NEXT_PATTERN
; *
; * Select the next pattern in sequence (and handle looping if we've reached PATTERN_LOOP_END
; * Execution falls through to PLAYNOTE to play the first note from our next pattern
; ********************************************************************************************************
NEXT_PATTERN:
                          LD   A,(PATTERN_PTR)
                          INC  A
                          INC  A
                          DEFB $FE                           ; CP n
PATTERN_LOOP_END:         DEFB 0
                          JR   NZ,NO_PATTERN_LOOP
                          DEFB $3E                           ; LD A,n
PATTERN_LOOP_BEGIN:       DEFB 0
                          POP  HL
                          EI
                          RET
NO_PATTERN_LOOP:          LD   (PATTERN_PTR),A
			                    DEFB $21                            ; LD HL,nn
PATTERNDATA1:             DEFW $0000
                          LD   E,A                            ; (this is the first byte of the pattern)
                          LD   D,0                            ; and store it at TEMPO
                          ADD  HL,DE
                          LD   E,(HL)
                          INC  HL
                          LD   D,(HL)
                          LD   A,(DE)                         ; Pattern Tempo -> A
	                	      LD   (TEMPO),A                      ; Store it at TEMPO

                          LD   A,1
                          LD   (NOTE_PTR),A

PLAYNOTE: 
			                    DEFB $21                            ; LD HL,nn
PATTERNDATA2:             DEFW $0000
                          LD   A,(PATTERN_PTR)
                          LD   E,A
                          LD   D,0
                          ADD  HL,DE
                          LD   E,(HL)
                          INC  HL
                          LD   D,(HL)                         ; Now DE = Start of Pattern data
                          LD   A,(NOTE_PTR)
                          LD   L,A
                          LD   H,0
                          ADD  HL,DE                          ; Now HL = address of note data
                          LD   D,(HL)
                          LD   E,1

; IF D = $0 then we are at the end of the pattern so increment PATTERN_PTR by 2 and set NOTE_PTR=0
                          LD   A,D
                          AND  A                              ; Optimised CP 0
                          JR   Z,NEXT_PATTERN

                          PUSH DE
                          INC  HL
                          LD   D,(HL)
                          LD   E,1

                          LD   A,(NOTE_PTR)
                          INC  A
                          INC  A
                          LD   (NOTE_PTR),A                   ; Increment the note pointer by 2 (one note per chan)

                          POP  HL                             ; Now CH1 freq is in HL, and CH2 freq is in DE

                          LD   A,H
                          DEC  A
                          JR   NZ,OUTPUT_NOTE

                          LD   A,D                            ; executed only if Channel 2 contains a rest
                          DEC  A                              ; if DE (CH1 note) is also a rest then..
                          JR   Z,PLAY_SILENCE                 ; Play silence

OUTPUT_NOTE:              LD   A,(TEMPO)
                          LD   C,A
                          LD   B,0
                          LD   A,BORDER_COL
                          EX   AF,AF'
                          LD   A,BORDER_COL                   ; So now BC = TEMPO, A and A' = BORDER_COL
                          LD   IXH,D
                          LD   D,$10
EAE5:                     NOP
                          NOP
EAE7:                     EX   AF,AF'
                          DEC  E
                          OUT  ($FE),A
                          JR   NZ,EB04

                          LD   E,IXH
                          XOR  D
                          EX   AF,AF'
                          DEC  L
                          JP   NZ,EB0B

EAF5:                     OUT  ($FE),A
                          LD   L,H
                          XOR  D
                          DJNZ EAE5

                          INC  C
                          JP   NZ,EAE7

                          RET

EB04:
                          JR   Z,EB04
                          EX   AF,AF'
                          DEC  L
                          JP   Z,EAF5
EB0B:
                          OUT  ($FE),A
                          NOP
                          NOP
                          DJNZ EAE5
                          INC  C
                          JP   NZ,EAE7
                          RET

PLAY_SILENCE:
                          LD   A,(TEMPO)
                          CPL
                          LD   C,A
SILENCE_LOOP2:            PUSH BC
                          PUSH AF
                          LD   B,0
SILENCE_LOOP:             PUSH HL
                          LD   HL,0000
                          SRA  (HL)
                          SRA  (HL)
                          SRA  (HL)
                          NOP
                          POP  HL
                          DJNZ SILENCE_LOOP
                          DEC  C
                          JP   NZ,SILENCE_LOOP
                          POP  AF
                          POP  BC
                          RET


; *** DATA ***
BORDER_COL:               EQU $7
TEMPO:                    DEFB 230

MUSICDATA:
        		DEFB 0   ; Loop start point * 2
        		DEFB 2   ; Song Length * 2
PATTERNDATA:        	DEFW      PAT0

; *** Pattern data consists of pairs of frequency values CH1,CH2 with a single $0 to
; *** Mark the end of the pattern, and $01 for a rest
PAT0:
        		DEFB 230  ; Pattern tempo
        		DEFB 161,161
        		DEFB 161,161
        		DEFB 161,161 
        	;	DEFB 161,161
        		DEFB 171,171
        		DEFB 171,171
        	;	DEFB 171,171
        		DEFB 180,180
        		DEFB 180,180
        	;	DEFB 180,180
        		DEFB 192,192
        		DEFB 192,192
        		DEFB 192,192
        		DEFB 192,192
        		DEFB 192,192
        	;	DEFB 192,192
        		DEFB $0

3 x

User avatar
MonkZy
Dizzy
Posts: 89
Joined: Thu Feb 08, 2018 1:01 pm

Re: My new game - CSSCGC 2018 entry

Post by MonkZy » Wed Mar 07, 2018 1:52 pm

Oh poop,

Upon play testing there is a bug...

The first play works well, but I fear I may have not reset a counter that works the acceleration of the car. This means on second play you will have to wait until the score is around 200 or so before you will get an increase in speed...

ho hum, crap games and all that
0 x

User avatar
Ast A. Moore
Dynamite Dan
Posts: 1193
Joined: Mon Nov 13, 2017 3:16 pm

Re: My new game - CSSCGC 2018 entry

Post by Ast A. Moore » Wed Mar 07, 2018 2:27 pm

Excellent for the first attempt! Spotted a bit of self-modifying code in the listing. I approve.
0 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

Kweepa
Manic Miner
Posts: 215
Joined: Sat Feb 03, 2018 6:14 pm
Location: Austin, Texas

Re: My new game - CSSCGC 2018 entry

Post by Kweepa » Wed Mar 07, 2018 2:29 pm

100% machine code?!
0 x

User avatar
MonkZy
Dizzy
Posts: 89
Joined: Thu Feb 08, 2018 1:01 pm

Re: My new game - CSSCGC 2018 entry

Post by MonkZy » Wed Mar 07, 2018 3:04 pm

100% machine code, yes...100% well planned and well implemented machine code...no

Thanks go to Jonathan Cauldwell and bytes:chutney, http://wikiti.brandonw.net/ and a few other sources...and beepola was rather useful for stringing a few sad tones together..and of course several SCF members for the inspiration and support.

T'was much fun...later I will debug it some more. If anyone with graphics skills can make a better job of the LCD cells I am more than happy, just stay inside the original attribute squares used and I/You can slot the image straight in.

I now will officially post this game to the competition....still not actually done that.
0 x

User avatar
MonkZy
Dizzy
Posts: 89
Joined: Thu Feb 08, 2018 1:01 pm

Re: My new game - CSSCGC 2018 entry

Post by MonkZy » Wed Mar 07, 2018 4:05 pm

This game will not work on my Series 3 rubber keyed Spectrum :(

Can anyone test this on some real hardware for me? It could be my spectrum is on the blink...aged capacitors. The keys only respond on this game if i repeatedly press and get luck. I am hoping I have missed some quirk of the real hardware, I have heard people mention de-bouncing.
0 x

User avatar
Ast A. Moore
Dynamite Dan
Posts: 1193
Joined: Mon Nov 13, 2017 3:16 pm

Re: My new game - CSSCGC 2018 entry

Post by Ast A. Moore » Wed Mar 07, 2018 6:19 pm

Enable the Issue 2 Keyboard option in Fuse and see if you your problem is reproduced. I have a feeling your Speccy is fine. ;)
0 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

User avatar
Ast A. Moore
Dynamite Dan
Posts: 1193
Joined: Mon Nov 13, 2017 3:16 pm

Re: My new game - CSSCGC 2018 entry

Post by Ast A. Moore » Wed Mar 07, 2018 6:42 pm

When you read the keyboard, ignore the highest three bits. They are not guaranteed to be the same across different Spectrum logic board issues.

Add AND 31 after IN A,(C), and compare that value with the values you’re testing for also ANDed with 31. E.g.:

Code: Select all

keyloop:
	push bc			;preserve B for loop
	ld bc,65278         	;keyboard row v,c,x,z,caps
	in a,(c)            	;see what keys are pressed.
	and 31
lastkey:	
	cp 31		      	;compare new keypress with last keypress (modified with code)
	jp z,skipinput		;jump out if the keypress is the same as the last
	ld (lastkey+1),a	;modify cp line for new keypress		
	cp 29			;compare with 'z'
	jp nz,not_z		;if keypress is not 'z' move to next compare
	
	....
	
not_z:
	cp 27			;compare with 'x'
And so on throughout.
0 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

User avatar
MonkZy
Dizzy
Posts: 89
Joined: Thu Feb 08, 2018 1:01 pm

Re: My new game - CSSCGC 2018 entry

Post by MonkZy » Wed Mar 07, 2018 7:31 pm

This is a relief, from the point of view of my spectrum. I guess I get bonus points for sending in a game that is unusable on the actual machine I wrote it for :D

Thank you for this knowledge...I will bug fix this and re upload a fixed version for the ZXDB
0 x

User avatar
Ast A. Moore
Dynamite Dan
Posts: 1193
Joined: Mon Nov 13, 2017 3:16 pm

Re: My new game - CSSCGC 2018 entry

Post by Ast A. Moore » Wed Mar 07, 2018 7:55 pm

MonkZy wrote:
Wed Mar 07, 2018 7:31 pm
I guess I get bonus points for sending in a game that is unusable on the actual machine I wrote it for :D.
Half a point at the very least!
0 x
Every man should plant a tree, build a house, and write a ZX Spectrum game.

Author of A Yankee in Iraq, a 50 fps shoot-’em-up—the first game to utilize the floating bus on the +2A/+3,
and zasm Z80 Assembler syntax highlighter.

Post Reply