03-29-2011, 07:51 AM
Hmm... or maybe not. Something's not *quite* right still. The music will play once, but it doesn't exit properly at the end of the tune (or if you press a key) - the Spectrum locks up rather than ending play gracefully.
I'll paste what I've been messing around with, maybe someone else can see what the issue is:
I'll paste what I've been messing around with, maybe someone else can see what the issue is:
Code:
cls
while(1)
print "PRESS ANY KEY TO PLAY"
while inkey <> "": end while
while inkey = "": end while
while inkey <> "": end while
playmusic()
border 7
end while
sub playmusic()
asm
; ORG 64000
; *****************************************************************************
; * Special FX Music Player Engine
; *
; * Based on code written by Jonathan Smith for the Special FX game, Firefly.
; * Modified by Chris Cowley
; *
; * Produced by Beepola v1.06.01
; ******************************************************************************
START: DI
EXX
PUSH HL ; Preserve HL' for return to BASIC
CALL PLAY_MUSIC
IM 1
POP HL
EXX
EI
RET
PLAY_MUSIC:
CALL MAKE_VECTOR_TABLE
CALL INIT_ISR
IM 2
LD A,VECTOR_TABLE_LOC / 256
LD I,A
LD HL,MUSICDATA ; <- Pointer to Music Data. Change
; this to play a different song
LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD (NEXT_PATT_PTR),HL
LD (NEXT_PERC_PATT_PTR),DE
LD (SAVED_SP),SP
LD HL,PLAYER_ISR
LD ($FFF5),HL ; Set up the JP instruction to point
; to our player ISR
LD A,BORDER_CLR
LD (OUT_VAL1),A ; Beeper out values for chan1
LD (OUT_VAL2),A ; Beeper out values for chan1
LD (OUT_VAL3),A ; Beeper out values for chan2
LD (OUT_VAL4),A ; Beeper out values for chan2
CALL SET_NEXT_PATT
CALL SET_NEXT_PERC_PATT
LD C,$01
EXX
LD BC,$0101
EI
HALT
NEXT_NOTE: CALL C,LF02C
LD A,B
OR A
JR NZ,LEF99
LEF61: DEFB $21 ; LD HL,nn
CURR_PATT_CH1:
DEFW $0000 ; Current Pattern for chan1
LEF64: LD A,(HL)
OR A
JP M,LEB9B ; bit7 set - command (sustain, pattern end, etc)
CALL LF0F6
LD (LEFA2),A
SRL A
SRL A
SRL A
LD D,A
LD (LEFAD),A
XOR A
LD (LF02D),A
INC A
LD (LEFA4),A
LD A,(OUT_VAL1) ; Adjust the EAR and MIC bits of this
OR $18 ; port output value for the tone
LD (OUT_VAL1),A ; generator
INC HL
POST_MUTE1: LD A,(HL)
LD (LEF96),A
INC HL
LD (CURR_PATT_CH1),HL
DEFB $06 ; LD B,n
LEF96: DEFB $54
JR LEF9E
LEF99: LD A,$03
LEF9B: DEC A
JR NZ,LEF9B
LEF9E: DEC D
JR NZ,LEFB5
DEFB $16 ; LD D,n
LEFA2: DEFB $3B
DEFB $3E ; LD A,n
LEFA4: DEFB $07
LEFA5: DEC A
JR NZ,LEFA5
DEFB $3E ; LD A,n
OUT_VAL1: DEFB $0
OUT ($FE),A
DEFB $3E ; LD A,n
LEFAD: DEFB $01 ; frequency counter for CH1
LEFAE: DEC A
JR NZ,LEFAE
DEFB $3E ; LD A,n
OUT_VAL2: DEFB $00
OUT ($FE),A
LEFB5: LD A,C
OR A
JR NZ,LEFEF
DEFB $21 ; LD HL,nn
CURR_PATT_CH2: DEFW $0000 ; Current pattern for CH2
LEFBC: LD A,(HL)
OR A
JP M,LEBA6 ; bit7 set - command (sustain, pattern end, etc)
CALL LF0F6
LD (LEFF9),A
SRL A
SRL A
LD (LF004),A
LD E,A
XOR A
LD (LF045),A
INC A
LD (LEFFB),A
LD A,(OUT_VAL3)
OR $18
LD (OUT_VAL3),A
INC HL
POST_MUTE2: LD A,(HL)
LD (LEFEC),A
INC HL
LD (CURR_PATT_CH2),HL
DEFB $0E ; LD C,n
LEFEC: DEFB $0E
JR LEFF4
LEFEF: LD A,$03
LEFF1: DEC A
JR NZ,LEFF1
LEFF4: DEC E
JP NZ,NEXT_NOTE
DEFB $1E ; LD E,n
LEFF9: DEFB $3B
DEFB $3E ; LD A,n
LEFFB: DEFB $02
LEFFC: DEC A
JR NZ,LEFFC
DEFB $3E ; LD A,n
OUT_VAL3: DEFB $18
OUT ($FE),A
DEFB $3E ; LD A,n
LF004: DEFB $0D ; ch2 note frequency
LF005: DEC A
JR NZ,LF005
DEFB $3E
OUT_VAL4: DEFB $0
OUT ($FE),A
JP NEXT_NOTE
LF00F:
POP HL
LD A,(HL)
LD (LF033),A
INC HL
JP LEF64
LF018: POP HL
LD A,(HL)
LD (LF04B),A
INC HL
JR LEFBC
MUTE_CH1: POP HL
LD A,(OUT_VAL1)
AND $07
LD (OUT_VAL1),A
JP POST_MUTE1
MUTE_CH2: POP HL
LD A,(OUT_VAL3)
AND $07
LD (OUT_VAL3),A
JP POST_MUTE2
NULL_NOTE1: POP HL
JP POST_MUTE1
NULL_NOTE2: POP HL
JP POST_MUTE2
LF021:
POP HL
CALL SET_NEXT_PATT
JP LEF61
LF02C: DEFB $3E
LF02D: DEFB $00
INC A
LD (LF02D),A
DEFB $FE ; CP
LF033: DEFB $02
JR C,LF044
XOR A
LD (LF02D),A
LD HL,LEFAD
DEC (HL)
JR Z,LF043
LD HL,LEFA4
LF043: INC (HL)
LF044: DEFB $3E ; LD A,n
LF045: DEFB $00
INC A
LD (LF045),A
DEFB $FE ; CP
LF04B: DEFB $04
RET C
XOR A
LD (LF045),A
LD HL,LF004
DEC (HL)
JR Z,LF05A
LD HL,LEFFB
LF05A: INC (HL)
RET
LF0F6: PUSH HL
PUSH DE
LD HL,FREQ_TABLE
LD E,A
LD D,$0
ADD HL,DE
LD A,(HL)
POP DE
POP HL
RET
; ** Reads and sets up the next melody pattern to play
SET_NEXT_PATT:
DEFB $21 ; LD HL,nn
NEXT_PATT_PTR: DEFW $0000 ; holds a pointer to the
; next pattern in the patter list
GET_PATT_ADDR: LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD A,E
OR D
JR NZ,STORE_NEXT_P
JP ISR_KEY_PRESSED
STORE_NEXT_P: LD (NEXT_PATT_PTR),HL
EX DE,HL
LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD (CURR_PATT_CH1),HL
LD (CURR_PATT_CH2),DE
RET
; ** Reads and sets up the next percussion pattern to play
SET_NEXT_PERC_PATT:
DEFB $21 ; LD HL,nn
NEXT_PERC_PATT_PTR:
DEFW $0000
GET_PERC_ADDR: LD E,(HL)
INC HL
LD D,(HL)
INC HL
LD (NEXT_PERC_PATT_PTR),HL
LD (PERC_PATT),DE
LD A,E
OR D
RET NZ
LD E,(HL)
INC HL
LD D,(HL)
EX DE,HL
JR GET_PERC_ADDR
CALL SET_NEXT_PERC_PATT
JR PLAY_PERC
LEB9B:
INC HL
PUSH HL
AND $7F
CALL JUMP_PERC_ADDR
DEFW LF021
DEFW LF00F
DEFW MUTE_CH1
DEFW NULL_NOTE1
LEBA6: INC HL
PUSH HL
AND $7F
CALL JUMP_PERC_ADDR
DEFW LF021
DEFW LF018
DEFW MUTE_CH2
DEFW NULL_NOTE2
PLAY_PERC:
DEFB $21 ; LD HL,nn
PERC_PATT: DEFW $0000 ; Address of the percussion data
LD A,(HL)
INC HL
LD C,(HL)
INC HL
LD (PERC_PATT),HL ; Point PERC_PATT at next datum
AND $7F
CALL JUMP_PERC_ADDR
DEFW DRUM00 ; F094
DEFW DRUM01 ; F09A
DEFW DRUM02 ; F0AC
DEFW DRUM03 ; F0BB
DEFW DRUM04 ; F0D4
DEFW DRUM05 ; F07C
JUMP_PERC_ADDR:
POP HL ; F0ED
ADD A,A
ADD A,L
LD L,A
LD A,(HL)
INC HL
LD H,(HL)
LD L,A
JP (HL)
DRUM00: CALL SET_NEXT_PERC_PATT
JR PLAY_PERC
DRUM01: LD E,$0A
LD A,BORDER_CLR
LD HL,$0100
DRUM01NOISE: XOR $18
OUT ($FE),A
LD B,(HL)
DRUM01LOOP: DJNZ DRUM01LOOP
INC HL
DEC E
JR NZ,DRUM01NOISE
RET
DRUM02: LD HL,$005A
DRUM02LOOP: LD A,(HL)
OR A
RET Z
AND $18
OR BORDER_CLR
OUT ($FE),A
INC HL
JR DRUM02LOOP
DRUM03: LD HL,$0F18
LD D,$0A
DRUM03LOOP3: LD B,(HL)
DRUM03LOOP: DJNZ DRUM03LOOP
LD A,$18
OR BORDER_CLR
OUT ($FE),A
INC HL
LD B,(HL)
DRUM03LOOP2: DJNZ DRUM03LOOP2
LD A,BORDER_CLR
OUT ($FE),A
INC HL
DEC D
JR NZ,DRUM03LOOP3
RET
DRUM04: LD E,$3F
LD D,$05
DRUM04LOOP3: LD B,E
DRUM04LOOP: DJNZ DRUM04LOOP
LD A,$18
OR BORDER_CLR
OUT ($FE),A
LD A,E
RRCA
LD E,A
LD B,A
DRUM04LOOP2: DJNZ DRUM04LOOP2
LD A,BORDER_CLR
OUT ($FE),A
DEC D
JR NZ,DRUM04LOOP3
DRUM05: RET
; ** Creates a vector table of 257 0xFF bytes at the location specified
; ** by VECTOR_TABLE_LOC
MAKE_VECTOR_TABLE:
LD HL,VECTOR_TABLE_LOC
LD DE,VECTOR_TABLE_LOC + 1
LD BC,$0100
LD (HL),$FF
LDIR
RET
; *** The IM 2 service routine active throughout the life of the player
; *** updates counters, plays any active percussion sounds, and checks for
; *** keypresses or Kempston joystick fire button to terminate
PLAYER_ISR:
PUSH AF
PUSH DE
PUSH HL
DEC C
DEC B
EXX
DEC C
CALL Z,PLAY_PERC ; EBB1
; Read keyboard
XOR A
IN A,($FE)
CPL
AND $1F
JR NZ,ISR_KEY_PRESSED
XOR A
IN A,($1F)
BIT 5,A
JR NZ,END_PLAYER_ISR
BIT 4,A
JR NZ,ISR_FIRE_PRESSED
END_PLAYER_ISR: EXX
POP HL
POP DE
POP AF
SCF
EI
RETI
ISR_FIRE_PRESSED:
ISR_KEY_PRESSED:
DEFB $31 ; LD SP,nn
SAVED_SP: DEFW $0000 ;
EI
RETI
; ** Sets up everything for our IM2 service routine. Specifically, copies a JR
; ** instruction to $FFFF and a JP $F0FF to $FFF4
INIT_ISR:
LD HL,$FFFF
LD (HL),$18 ; Copies in our JR for JR FFF4
LD HL,$FFF4
LD (HL),$C3 ; JP (jump address filled-in
; during player initialization)
RET
; *** DATA ***
VECTOR_TABLE_LOC EQU $FE00
BORDER_CLR EQU $0
FREQ_TABLE: DEFB $FD,$EE,$E1,$D4,$C8,$BD,$B2,$A8,$9F,$96,$8E,$86,$7E
DEFB $77,$70,$6A,$64,$5E,$59,$54,$4F,$4B,$47,$43,$3F
DEFB $3B,$38,$35,$32,$2F,$2C,$2A,$27,$25,$23,$21,$1F
DEFB $1D,$1C,$1B,$19,$17,$16,$15,$13,$12,$11,$10,$0F
DEFB $0E,$0D,$0C,$01,$00
MUSICDATA: DEFW PERCSTART
SONGSTART: DEFW PAT1
DEFW $0000
DEFW SONGSTART
PAT1: DEFW PAT1C2
DEFB 6,14
DEFB 10,14
DEFB 8,7
DEFB 10,7
DEFB 11,7
DEFB 8,7
DEFB 6,14
DEFB 10,14
DEFB 8,7
DEFB 10,7
DEFB 11,7
DEFB 8,7
DEFB $80
PAT1C2:
DEFB 23,7
DEFB 22,7
DEFB 20,7
DEFB 22,7
DEFB 23,7
DEFB 23,7
DEFB 22,7
DEFB 20,7
DEFB 23,7
DEFB 22,7
DEFB 20,7
DEFB 22,7
DEFB 23,7
DEFB 23,7
DEFB 22,7
DEFB 20,7
DEFB $80
PERCSTART: DEFW DRM1
DEFW $0000
DEFW PERCSTART
DRM1:
DEFB 129,28
DEFB 131,28
DEFB 129,28
DEFB 131,28
DEFB $80
end asm
end sub