; ==================================================================================================
; === RICKY GERVAIS' SIMON: for the 1K ZX80 (with 4K ROM BASIC)
; === Written by John Connolly for the 2021 comp.sys.sinclair Crap Games Competition
; ==================================================================================================

; Set tab stops to 4 to view this file properly!

; This assembly listing produces a .O/.80 format tape file. These formats are essentially a
; representation of the ZX80's memory, from $4000 to E_LINE - 1, thus they contain the
; entire state of the system (minus the DFILE).

; Many thanks to Martin Korth, upon whose NO$ZX specification document I relied upon heavily
; during the development of this program.

org $4000										; Start of ZX80 memory

; === ZX80 SYSTEM VARIABLES

ERR_NR:		db $ff								; Error no. (report code - 1)
FLAGS:		db $88								; Flags
PPC:		dw $fffe							; Next line number to execute
P_PTR:		dw $4034							; Position of edit cursor in RAM
E_PPC:		dw $000a							; Line number of selection cursor
VARS:		dw PRG_VARS							; Address of start of variable area
E_LINE:		dw PRG_END							; Address of start of edit line
D_FILE:		dw PRG_END+2						; Start of display file
DF_EA:		dw PRG_END+3						; Start of edit area of display file
DF_END:		dw PRG_END+3						; End of display file

DF_SZ:		db $02								; Number of lines in edit area
S_TOP:		dw $0a00							; Number of 1st line on screen
X_PTR:		dw $0000							; Address of character preceding [S] marker
OLDPPC:		dw $0000							; Line number for CONTINUE
FLAGX:		db $00								; More flags

T_ADDR:		dw $07b0							; Address of next item in syntax table
SEED:		dw $0000							; Seed for random number generator
FRAMES:		dw $0000							; Count of frames since boot-up
DEST:		dw $0000							; Address of variable in statement
RESULT:		dw $0000							; Value of the last expression
S_POSN_X:	db $21								; X pos. of screen printing cursor
S_POSN_Y:	db $17								; Y pos. of screen printing cursor
CH_ADD:		dw PRG_END+1						; Address of next BASIC char/token

; ZX80 ROM ROUTINES
ROM_RND:	equ $0BED

; === LINE 1
; This line will contain the machine code as a REM statement

PRG_LINE1:
	db $00, $01									; Line 1 (stored in big-endian format)

	db $fe										; REM

; === MACHINE CODE

MC_START:
	ld sp,$4400									; Initialise stack pointer
	
	ld hl,FRAMES
	inc (hl)									; Increment FRAMES counter
	
	call MC_GET_KEYS
	jr z,MC_START								; Jump back if keys not pressed

; This is the start of the game
MC_GAME_BEGIN:
	ld a,3										; 3 rounds to start
	ld (MC_ROUND),a

; This is the start of a new round
MC_GAME_PLAY_SEQ:
	ld de,$4050
	call MC_BEEP								; Start of round beep

	call MC_PREPARE_GAME

; This is the "play next step" loop
MC_GAME_PLAY_SEQ_L1:
	ld hl,3
	call ROM_RND								; Get random value (1-3)
	
	ld a,l
	call MC_RICKY_PLAY
	
	push hl
	pop hl
	djnz $-2									; Waste time

	dec iyh										; Decrement round counter
	jr nz,MC_GAME_PLAY_SEQ_L1					; Jump back if more steps left

; This is the start of the repeat section
MC_GAME_READ_SEQ:
	ld de,$4050
	call MC_BEEP
	ld de,$3036
	call MC_BEEP
	
	call MC_PREPARE_GAME

; This is the "repeat next step" loop
MC_GAME_READ_SEQ_L1:
	call MC_GET_KEYS_GAME						; Get next number from keys
	jr z,MC_GAME_FAIL							; Fail if no key pressed in time
	
	ld iyl,a									; Keep in temp. storage
	ld hl,3										; Bound randomness within 1-3
	call ROM_RND
	ld a,l
	cp iyl										; Does user input match next step?
	jr nz,MC_GAME_FAIL							; Fail if steps don't match
	
	call MC_RICKY_PLAY							; Play step if match
	
	dec iyh										; Decrement round counter
	jr nz,MC_GAME_READ_SEQ_L1					; Jump back if more steps left

MC_GAME_NEXT_ROUND:
	ld a,(MC_ROUND)
	inc a										; Increment round counter
	ld (MC_ROUND),a
	
	jr MC_GAME_PLAY_SEQ
	
MC_GAME_FAIL:
	ld de,$a0b0
	call MC_BEEP
	jr MC_START



MC_PREPARE_GAME:
	ld hl,(FRAMES)
	ld (SEED),hl								; Reset seed with initial value

	ld a,(MC_ROUND)
	ld iyh,a
	ret



MC_BEEP:
; Size:		22 bytes
; Inputs:	D - Number of loops to perform of beep loop
;			E - Delay value
	ld c,8

MC_BEEP_L1:
	ld l,d

MC_BEEP_L2:
	ld b,e
	in a,($fe)
	nop
	djnz $-1

	ld b,e
	out ($ff),a
	nop
	djnz $-1

	dec l
	jr nz,MC_BEEP_L2

	dec c
	jr nz,MC_BEEP_L1

	ret



MC_GET_KEYS:
; Size:		 8 bytes
; Outputs:	A - State of 1-5 half-row (inverted, bits 3-7 clear)
;			F - Flags (Z is set if no keys pressed)
	ld bc,$f7fe									; 1-5 half-row
	in a,(c)									; Get key state
	cpl											; Invert bits (i.e. 1 = pressed)
	and $07										; Keep 1 to 3 key bits only
	ret



MC_GET_KEYS_GAME:
; Ouputs:	A - State of 1-5 half-row (inverted, bits 3-7 clear)
;			F - Flags (Z is set if no keys pressed)
	ld de,60000									; Wait a little bit (1 to 2 secs.)

MC_GET_KEYS_GAME_L1:
	call MC_GET_KEYS							; Get 1-3 keys
	call nz,MC_GET_KEYS_GAME_DECIPHER			; Decode from key state to key number
	or a										; Key pressed?
	ret nz										; Return if so
	
	dec de										; Decrement counter
	ld a,e
	or d										; Zero?
	ret z										; Return if out of time
	jr MC_GET_KEYS_GAME_L1						; Loop back if time left

MC_GET_KEYS_GAME_DECIPHER:
	cp $01										; Key 1?
	ret z
	cp $02										; Key 2?
	ret z
	cp $04										; Key 3?
	ld a,$03									; Decode
	ret



MC_RICKY_PLAY:
; Size:		34 bytes
; Inputs:	A - Input number (1-3)
	add a,a
	add a,a
	add a,a
	add a,a										; Value (16-48)
	
	add a,58-32									; Delay of 58 = ~4000Hz
	ld c,a

	ld ix,MC_RICKY_DATA
	ld hl,MC_RICKY_DATA_LEN

MC_RICKY_PLAY_L1:
	ld d,(ix+0)									; Get byte from PCM data
	ld e,8										; 8 bits in a byte

MC_RICKY_PLAY_L2:
	rlc d										; Get next bit
	jr nc,MC_RICKY_PLAY_L3						; Jump over IN if bit disabled

	in a,($fe)									; MIC output set HIGH

MC_RICKY_PLAY_L3:
	ld b,c
	djnz $										; Delay loop (13T per iteration, except last)

	out ($ff),a									; MIC output set LOW

	dec e										; Decrement bit counter
	jr nz,MC_RICKY_PLAY_L2						; Get next byte if out of bits

	dec hl										; Decrement sample counter
	inc ix										; Increment data pointer

	ld a,l
	or h										; Is sample counter zero?
	jr nz,MC_RICKY_PLAY_L1						; Jump back if not

	ret

MC_RICKY_DATA:
	incbin "ricky.pcm"
MC_RICKY_DATA_LEN: equ $ - MC_RICKY_DATA

MC_ROUND:
	db 0

; === END OF LINE 1

	db $76

; === LINE 10
; This line will start the machine code off

PRG_LINE0:
	db $00, $0a									; Line 0 (stored in big-endian format)

	db $f4, $3a, $38, $37, $da
	db $1d, $22, $20, $1e, $23, $d9				; PRINT USR(16427)

	db $76										; HALT

; === VARIABLE AREA

PRG_VARS:
	db $80										; End of variable area marker

PRG_END:	equ $
