;---------------------------------------------------------
;                ZX81 DANCING DEMON
;
; This code is intend to be compiled with AS80.
; For it, type this command: as80 -x3 -odemon.p demon.a80
;


; Redefine ASCII to ZX81 characters

        cmap    " ",0
        cmap    "_",128
        cmap    '"',11
        cmap    "#",12
	cmap    "$",13
	cmap    ":",14
	cmap    "?",15
	cmap    "(",16
	cmap    ")",17
	cmap    ">",18
	cmap    "<",19
	cmap    "=",20
	cmap    "+",21
	cmap    "-",22
	cmap    "*",23
	cmap    "/",24
	cmap    ";",25
	cmap    ",",26
	cmap    ".",27
        cmap    "0",28,29,30,31,32,33,34,35,36,37
	cmap    "A",38,39,40,41,42,43,44,45,46,47,48,49,50
	cmap    "N",51,52,53,54,55,56,57,58,59,60,61,62,63
	cmap    "a",166,167,168,169,170,171,172,173,174,175,176,177,178		;reverse letters
	cmap    "n",179,180,181,182,183,184,185,186,187,188,189,190,191

; some useful ROM routines

RESET		equ	$0000
SLOWFAST	equ	$0207
SETFAST		equ	$02E7
NEXTLINE	equ	$0676
DECODEKEY	equ	$07bd
PRINTAT		equ	$08f5
MAKEROOM	equ	$099e
LINE_ADDR	equ	$09d8
CLS		equ	$0a2a
STACK2BC	equ	$0bf5
STACK2A		equ	$0c02
CLASS6		equ	$0d92
FINDINT		equ	$0ea7
FAST		equ	$0f23
SLOW		equ	$0f2b
BREAK1		equ	$0f46
DEBOUNCE	equ	$0f4b
SETMIN		equ	$14BC

        org     $4009

;= System variables ============================================

VERSN:  db      0
E_PPC:  dw      0               ; BASIC line number of line with cursor.
D_FILE: dw      dfile
DF_CC:  dw      dfile+1
VARS:   dw      var
DEST:   dw      0
E_LINE: dw      var+1
CH_ADD: dw      last-1
X_PTR:  dw      0
STKBOT: dw      last
STKEND: dw      last
BERG:   db      0
MEM:    dw      membot
SPARE1: db      0               ; not used
DF_SZ:  db      2               ; Number of lines in lower part of screen.
S_TOP:  dw      1               ; BASIC line number of line at top of screen.
LAST_K: db      $ff,$ff,$ff
MARGIN: db      55
NXTLIN: dw      dfile           ; Memory address of next program line to be executed.
OLDPPC: dw      0
FLAGX:  db      0
STRLEN: dw      0
T_ADDR: dw      $0c8c
SEED:   dw      0
FRAMES: dw      $ffff
COORDS: db      0,0
PR_CC:  db      $bc
S_POSN: db      33,24
CDFLAG: db      %01000000       ;
        ds      33              ;Print buffer
membot: ds      30              ;Calculator?s memory area
SPARE2: ds      2               ;not used

;= First BASIC line, asm code ==================================

line0:  db      0,0             ;line number
        dw      dfile-$-2       ;line length
        db      $ea		;REM
        db      "dancing_demon"
        db      $76,$76

; -------------------------------------------------------------------------------------------------

beat		equ 5

dance1:
	db "JJJFGFGFGFGDEDEFGFGLMFMLGLMFMLGDEDEFGFGAAAAAAAZAAAAEDEDTTQQQQRRRRDEDEFGJ."
music1:
	db "EFHJZIZHZZZJJIIHZZJMMLLJJHFEZCZAEFHJZIZHZZHJJIIHZZJMMLLJJHFEZCZA"
        db "MMJMZZZZMMMMZHZZMMJMZZZZMMMMHHGFFECJZIZHZZHJJIIHZZJMMLLJJHFEZCZA."

dance2:
	db "JJFJGJFJGJDSESFJGJQLQLFJGJRRRRRRJJKYJRVRVRVRVBXHHYXQMQMQMQMIKJIKJFJGJFJGJDJEJFJK."
music2:
	db "EFHZAZQOMZEZLJHZZCHFEZZZEFHZAZQOMZEZLJHZZCFEAZZZCEFZOMLJHZMZLMOZ"
        db "ZCLJHZGFEFHZAZMMLJZZCEHZZHFCEZZZEFHZAZMHLJZZCEHZZHJLMZZZ."

op_dance:
        db "SSSSZ:."

applause:
	db "::."

nomusic:
	db "Z."

cursor:
	dw 0

music_addr:
	dw 0


; CLEAR SCREEN 
; ============
;
;clrscr:
;	ld hl,(D_FILE)
;	ld c,$18
;clrscr1:
;	ld b,$20
;	xor a
;clrscr2:
;	inc hl
;	ld (hl),a
;	djnz clrscr2
;	inc hl
;	dec c
;	jr nz,clrscr1
;	ret

; FLOOR
; =====
;
;floor:
;	ld hl,(D_FILE)
;        ld de,21*33+2
;        add hl,de
;        ld (hl),$81
;        inc hl
;        ld b,28
;floor_loop1:
;        ld (hl),$8a
;        inc hl
;        djnz floor_loop1
;        ld (hl),$82
;        inc hl
;        inc hl
;        inc hl
;	ld (hl),$81
;	inc hl
;	ld b,30
;floor_loop2:
;	ld (hl),$80
;	inc hl
;	djnz floor_loop2
;	ld (hl),$82
;	inc hl
;        inc hl
;	ld de,Title
;	ld bc,30
;	ex de,hl
;	ldir
;	ret

; BLIT
; ====

blit:
				; HL = pointer to the screen
				; DE = pointer to the frame

	ld a,0

	ld b,16
blit_topclear:
	ld (hl),a
	inc hl
	djnz blit_topclear	; B = 0

	ld a,15
blit_loop:
	ld c,16			; bytes in line (must be 16!)
	add hl,bc
        inc hl                  ; bypass the newline character
	ex de,hl
	ldir
	ex de,hl

	dec a
	jr nz,blit_loop

	ld c,16
	add hl,bc
        inc hl

        push hl
        ld hl,(D_FILE)
        ld de,21*33
        add hl,de
        pop de
        xor a
        sbc hl,de
        ret c
        ex de,hl

	ld b,c
blit_botclear:
	ld (hl),a
	inc hl
	djnz blit_botclear	; B = 0

	ret

; CLICK
; =====
click:

        ld bc,$fe07		;enable noise in channel A
        call write_ay

        ld b,100
clk_loop:
        djnz clk_loop
        ret


; EXEC
; ====

exec: 
	pop hl			; top of stack = pointer to the
				; dance script

exec_loop:
	ld a,(hl)
	inc hl

	or a
	jr z,play_loop		; break loop if low byte of ram address is 0
	ld e,a

	ld a,(hl)
	inc hl

	ld c,a
	and $7f
	ld d,a

	ld b,(hl)
	inc hl

	bit 7,b			; if top bit of beat byte was set, play a note
        res 7,b
	jr z,exec_delay
	exx
	ld a,(de)
	inc de
        cp $1b
        jr nz,ply_nt
        ld de,(music_addr)
        ld a,(de)
        inc de
ply_nt:
        call play_note
	exx

exec_delay:
        ld a,80
subdly: dec a
	halt
        jr nz,subdly
	djnz exec_delay		; B = 0

	bit 7,c			; if top bit of ram address was set, click
	call nz,click

	ld bc,$ff07		; stop sound out
	call write_ay


	ld a,(hl)
	inc hl

	or a
	ld c,a
	ld b,0

	jp p,exec_plus		; argh...
	ld b,$ff
exec_plus:

	push hl
	ld hl,(cursor)
	add hl,bc
        ld a,(hl)		; doesn't allow that the demon exceed the screen
        cp $76			;
        jr z,adj		;
        ld bc,15		;
        add hl,bc		;
        ld a,(hl)		;
        cp $76			;
        jr nz,adj		;
        dec hl
adj:	or a			;
        sbc hl,bc        	;
	ld (cursor),hl
	call blit

	pop hl
	jr exec_loop

; PLAY
; ====
	
play:
	call set_ay
play_loop:
	call BREAK1		; BREAK key is pressed?
	jr c,play1		; to play1 if not
release:
	call BREAK1
	jr nc,release
	ret
play1:
	exx
	ld a,(hl)		; HL' = pointer to the dance routine string
	inc hl
	exx
	cp $1b			; end character
	ret z
;	jr script_A


        include "demon_scripts.inc"	


; MAIN
; ====

play_usdance:
        ld d,$52		;var M$
        call srch_var
	ld (music_addr),hl
        push hl
        ld d,$49		;var D$
        call srch_var
	pop de
	jr main

play_dance1:
	ld hl,dance1
	ld de,music1
	ld (music_addr),de
	jr main

play_dance2:
	ld hl,dance2
	ld de,music2
	ld (music_addr),de

main:
	exx
	push hl
	push de
	push bc
	ld hl,(D_FILE)
        ld de,5*33+8+1  ;cursor_start
        add hl,de
	ld (cursor),hl
	call play
	pop bc
	pop de
	pop hl
	exx
	ret


        include "demon_frames.inc"


show_applause:
	ld hl,applause
	ld de,nomusic
	ld (music_addr),de
show_app1:
        exx
	push hl
	push de
	push bc
	call play
	pop bc
	pop de
	pop hl
	exx
	ret

opening:
	ld hl,(D_FILE)
        ld de,5*33+12+1  ;cursor_start
        add hl,de
	ld (cursor),hl
        ld hl,op_dance
        ld de,nomusic
	ld (music_addr),de
	jr show_app1


srch_var:
        ld hl,($4010)
nxt_var:
        ld a,(hl)
        cp $80
        jr z,var_nf
        cp d
        jr nz,chk_fnv
        inc hl
        inc hl
        inc hl
        ret     

var_nf:
        rst $08
        db $01

chk_fnv:
        and $e0
        cp $e0		;is it a FOR-NEXT var?
        jr nz,chk_olnv
        ld bc,$0012
adv_var:
        add hl,bc
        jr nxt_var

chk_olnv:
        cp $60		;is it a one letter numeric var?
        jr nz,chk_mlnv
adv_fpn:
        ld bc,$0006
        jr adv_var

chk_mlnv:
        cp $a0		;is it a multiple letter numeric var?
        jr nz,dim_var
nxt_chr:
        inc hl
        bit 7,(hl)
        jr z,nxt_chr
        jr adv_fpn

dim_var:		;so it is a dimensional var
        inc hl
        ld c,(hl)
        inc hl
        ld b,(hl)
        inc hl
        jr adv_var


; AY routines:

write_ay:
        ld a,c
        out ($df),a             ;set AY register
        ld a,b
        out ($0f),a             ;write data to AY register
        ret 

play_music:
        ld d,$52		;var M$
        call srch_var
        call set_ay
nxt_note:
        ld a,(hl)
        inc hl
        cp $1b
        ret z
        call play_note
        call duration
        call duration
        jr nxt_note

play_keynote:
        call set_ay
        ld a,0			;load key code in accumulator
	call play_note
duration:
        ld bc,$0025
nt_dur:
        djnz nt_dur
        dec c
        jr nz,nt_dur
        ld bc,$ff07		;disable tone in all channels
        call write_ay
	ret

play_note:
        push hl
        push de
        sub a,$26
        rla
        ld e,a
        ld d,0
        ld hl,notes
        add hl,de
        ld c,$02		;set fine tune for channel B
        ld b,(hl)
        call write_ay
        inc hl
        inc c			;set coarse tune for channel B
        ld b,(hl)
        call write_ay
        ld bc,$fd07		;enable tone in channel B
        call write_ay
        pop de
        pop hl
        ret

set_ay:
        ld bc,$ff07		;disable all chanels outputs
        call write_ay
        ld bc,$0000		;set chanel A to zero
        call write_ay
        ld bc,$0001
        call write_ay
        ld bc,$0f08		;set volume in chanel A to 15
        call write_ay
        ld bc,$0c09		;set volume in chanel B to 12
        call write_ay
        ret

notes:
        dw $0184		;C1
        dw $016e		;C1#
        dw $0159		;D1
        dw $0146		;D1#
        dw $0134		;E1
        dw $0122		;F1
        dw $0112		;F1#
        dw $0103		;G1
        dw $00f4		;G1#
        dw $00e6		;A1
        dw $00d9		;A1#
        dw $00cd		;B1
        dw $00c2		;C2
        dw $00b7		;C2#
        dw $00ac		;D2
        dw $00a3		;D2#
        dw $009a		;E2
        dw $0091		;F2
        dw $0089		;F2#
        dw $0081		;G2
        dw $007a		;G2#
        dw $0073		;A2
        dw $006c		;A2#
        dw $0066		;B2
        dw $0061		;C3
        dw $0000		;pause

; -------------------------------------------------------------------------------------------------
        db      $76             ;N/L (end of line 0)


;- Display file --------------------------------------------

dfile:  db      $76
        ds      32              ;screen line 0
        db      $76
        ds      32              ;screen line 1
        db      $76
        ds      32              ;screen line 2
        db      $76
        ds      32              ;screen line 3
        db      $76
        ds      32              ;screen line 4
        db      $76
        ds      32              ;screen line 5
        db      $76
        ds      32              ;screen line 6
        db      $76
        ds      32              ;screen line 7
        db      $76
        ds      32              ;screen line 8
        db      $76
        ds      32              ;screen line 9
        db      $76
        ds      32              ;screen line 10
        db      $76
        ds      32              ;screen line 11
        db      $76
        ds      32              ;screen line 12
        db      $76
        ds      32              ;screen line 13
        db      $76
        ds      32              ;screen line 14
        db      $76
        ds      32              ;screen line 15
        db      $76
        ds      32              ;screen line 16
        db      $76
        ds      32              ;screen line 17
        db      $76
        ds      32              ;screen line 18
        db      $76
        ds      32              ;screen line 19
        db      $76
        ds      32              ;screen line 20
        db      $76
        ds      32              ;screen line 21
        db      $76
        ds      32              ;screen line 22
        db      $76
        ds      32              ;screen line 23
        db      $76

;- BASIC-Variables ----------------------------------------

var:    db      $80

;- End of program area ----------------------------

last:

        end
