My new game - CSSCGC 2018 entry
Posted: 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 :
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 :
Spoiler
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