aboutsummaryrefslogtreecommitdiff
path: root/src-z80/core/bgm.z80
diff options
context:
space:
mode:
Diffstat (limited to 'src-z80/core/bgm.z80')
-rw-r--r--src-z80/core/bgm.z80290
1 files changed, 290 insertions, 0 deletions
diff --git a/src-z80/core/bgm.z80 b/src-z80/core/bgm.z80
new file mode 100644
index 0000000..f69235d
--- /dev/null
+++ b/src-z80/core/bgm.z80
@@ -0,0 +1,290 @@
+;****************************************************************************
+; PlayBGM [command $04]
+; Plays a BGM
+;****************************************************************************
+
+PlayBGM:
+ PollPCM
+ call ClearBGM ; Clear BGM resources
+ PollPCM
+
+ ld a, (RAM_Status) ; Show BGM playback in Echo's status
+ or $02
+ ld (RAM_Status), a
+
+ PollPCM
+
+ ld hl, RAM_ComBank ; Get command parameters
+ ld c, (hl)
+ inc l
+ ld e, (hl)
+ inc l
+ ld d, (hl)
+
+ PollPCM
+
+ xor a ; Command parsed
+ ld (RAM_Command), a
+
+ ld hl, RAM_BGMData ; Set BGM as playing
+ ld (hl), $01
+ inc l ; No delays!
+ ld (hl), $01
+ inc l ; Store BGM start bank
+ ld (hl), c
+ inc l ; Store BGM start address (low)
+ ld (hl), e
+ inc l ; Store BGM start address (high)
+ ld (hl), d
+
+ PollPCM
+
+ ld hl, ProcessBGM ; Tell Echo to process BGM
+ ld (DoTick_BGM+1), hl
+
+ PollPCM
+ jp IdleLoop ; End of subroutine
+
+;****************************************************************************
+; ProcessBGM
+; Processes a tick for a BGM
+;****************************************************************************
+
+ProcessBGM:
+ PollPCM
+
+ ld hl, RAM_BGMData+1 ; BGM data address
+
+ ld a, (hl) ; Delaying?
+ dec a
+ jp z, .nodelay
+ ld (hl), a
+
+ jp DoTick_BGMSkip ; End of subroutine
+
+.nodelay:
+ PollPCM
+
+ inc l
+ ld c, (hl) ; Get current address
+ inc l
+ ld e, (hl)
+ inc l
+ ld d, (hl)
+ ex de, hl
+
+ProcessBGMRun:
+ PollPCM ; Fetch next event
+ call GetParam
+ PollPCM
+
+ ld a, b ; Parse byte
+
+ cp $08
+ jp c, NoteOnFMBGM ; Events $00-$07: note on FM
+ cp $0B
+ jp c, NoteOnPSGBGM ; Events $08-$0A: note on PSG (square)
+ jp z, NoteOnNoiseBGM ; Event $0B: note on PSG (noise)
+ cp $0C
+ jp z, PlayPCMBGM ; Event $0C: play PCM
+
+ PollPCM
+ ld a, b
+
+ cp $18
+ jp c, NoteOffFMBGM ; Events $10-$17: note off FM
+ cp $1C
+ jp c, NoteOffPSGBGM ; Events $18-$1B: note off PSG
+ jp z, StopPCMBGM ; Event $1C: note off PCM
+
+ PollPCM
+ ld a, b
+
+ cp $FE
+ jp z, SetDelayBGM ; Event $FE: set delay
+ cp $FF
+ jp z, StopBGMEvent ; Event $FF: stop BGM
+ cp $FC
+ jp z, LoopBGM ; Event $FC: loop BGM
+ cp $FD
+ jp z, SetLoopBGM ; Event $FD: set loop point
+
+ PollPCM
+ ld a, b
+
+ cp $28
+ jp c, SetFMVolBGM ; Events $28-$2B: set FM volume
+ cp $2C
+ jp c, SetPSGVolBGM ; Events $28-$2B: set PSG volume
+
+ PollPCM
+ ld a, b
+
+ cp $38
+ jp c, SetNoteFMBGM ; Events $30-$37: set FM note
+ cp $3B
+ jp c, SetNotePSGBGM ; Events $38-$3A: set PSG note (square)
+ jp z, SetNoteNoiseBGM ; Event $3B: set PSG note (noise)
+
+ PollPCM
+ ld a, b
+
+ cp $48
+ jp c, LoadFMBGM ; Events $40-$47: load FM instrument
+ cp $4C
+ jp c, LoadPSGBGM ; Events $48-$4B: load PSG instrument
+
+ PollPCM
+ ld a, b
+
+ cp $F8 ; Events $F0-$F7: set FM parameters
+ jp c, SetFMParamBGM
+
+ PollPCM ; FFFFFFFFF bad event >:(
+ jp StopBGMEvent ; End of subroutine
+
+ProcessBGMSkip2: ; This is where we land after a locked event
+ PollPCM ; that had two bytes for the parameter
+ inc l ; Skip first byte
+ jp nz, .nobankskip2
+ inc h
+ jp nz, .nobankskip2
+ ld h, $80
+ inc c
+.nobankskip2:
+
+ProcessBGMSkip1: ; This is where we land after a locked event
+ PollPCM ; that had one byte for the parameter
+ inc l ; Skip byte
+ jp nz, .nobankskip1
+ inc h
+ jp nz, .nobankskip1
+ ld h, $80
+ inc c
+.nobankskip1:
+
+ProcessBGMSkip: ; This is where we land after a locked event
+ PollPCM ; without parameters
+
+ jp ProcessBGMRun ; Keep processing
+
+;****************************************************************************
+; StopBGM* [command $05, event $FF]
+; Stops BGM playback
+;****************************************************************************
+
+StopBGMEvent:
+ call StopBGM ; We're just a wrapper
+ jp DoTick_BGMSkip ; End of subroutine
+
+StopBGMCmd:
+ xor a ; Command parsed
+ ld (RAM_Command), a
+ call StopBGM ; We're just a wrapper
+ jp IdleLoop ; End of subroutine
+
+StopBGM:
+ ld a, (RAM_Status) ; Hide BGM playback in Echo's status
+ and $FD
+ ld (RAM_Status), a
+
+ PollPCM
+
+ call ClearBGM ; Clear BGM resources
+ PollPCM
+
+ xor a ; Stop playback
+ ld (RAM_BGMPlaying), a
+ ld hl, DoTick_BGMSkip
+ ld (DoTick_BGM+1), hl
+
+ ret ; End of subroutine
+
+;****************************************************************************
+; ClearBGM
+; Clears BGM resources
+;****************************************************************************
+
+ClearBGM:
+ ld a, (RAM_Locked+6) ; Stop PCM playback if needed
+ or a
+ call z, StopPCM
+
+ ld b, 4 ; Mute all non-locked PSG channels
+ ld de, RAM_PSGData+48
+ ld hl, RAM_Locked+11
+.mutepsg:
+ PollPCM
+ ld a, (hl)
+ or a
+ jr nz, .nopsgmute
+ xor a
+ ld (de), a
+.nopsgmute:
+ PollPCM
+ ld a, e
+ sub 16
+ ld e, a
+ dec l
+ djnz .mutepsg
+
+ ld b, 8 ; Mute all non-locked FM channels
+.mutefm:
+ PollPCM
+ ld a, (hl)
+ or a
+ jr nz, .nofmmute
+ PollPCM
+ dec b
+ ld (ix+0), $28
+ ld (ix+1), b
+ inc b
+.nofmmute:
+ dec l
+ djnz .mutefm
+
+ ret ; End of subroutine
+
+;****************************************************************************
+; LoopBGM [event $FC]
+; Makes a BGM loop
+;****************************************************************************
+
+LoopBGM:
+ PollPCM
+
+ ex de, hl ; Get looping address
+ inc l
+ ld c, (hl)
+ inc l
+ ld e, (hl)
+ inc l
+ ld d, (hl)
+ dec l
+ dec l
+ dec l
+ ex de, hl
+
+ jp ProcessBGMRun ; End of subroutine
+
+;****************************************************************************
+; SetLoopBGM [event $FD]
+; Sets the BGM loop point
+;****************************************************************************
+
+SetLoopBGM:
+ PollPCM
+
+ ex de, hl
+ inc l ; Store loop point address
+ ld (hl), c
+ inc l
+ ld (hl), e
+ inc l
+ ld (hl), d
+ dec l
+ dec l
+ dec l
+ ex de, hl
+
+ jp ProcessBGMRun ; End of subroutine