diff options
Diffstat (limited to 'src-z80/core/main.z80')
| -rw-r--r-- | src-z80/core/main.z80 | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/src-z80/core/main.z80 b/src-z80/core/main.z80 new file mode 100644 index 0000000..4018a1b --- /dev/null +++ b/src-z80/core/main.z80 @@ -0,0 +1,330 @@ +;**************************************************************************** +; EntryPoint +; Where the program starts +;**************************************************************************** + +EntryPoint: + ld sp, RAM_Stack ; Init stack + + xor a ; Reset Echo status + ld (RAM_Status), a + + ld hl, $7F11 ; Mute PSG + ld (hl), $9F + ld (hl), $BF + ld (hl), $DF + ld (hl), $FF + xor a + ld (RAM_PSGData), a + ld (RAM_PSGData+16), a + ld (RAM_PSGData+32), a + ld (RAM_PSGData+48), a + + ld hl, $6000 ; Set default bank + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + ld (hl), l + + ;ex af, af' ; Set 9th bank bit to 0 by default + ;xor a ; (0 = $000000-$7FFFFF range aka MD mode) + ;ex af, af' ; (1 = $800000-$FFFFFF range aka 32X mode) + + ld ix, $4000 ; YM2612 I/O ports base address + ld iyh, $40 + + exx ; Init PCM playback status + ld bc, $0000 ; B = playing, C = bank + ld de, $0000 ; DE = address + ld hl, $6000 ; HL = always $6000 + exx + + ld (ix+0), $2B ; Disable DAC by default + ld (ix+1), $00 + + ld e, $7F ; Mute all FM channels + ld a, $40 + ld b, 4 +.mutefm: + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + ld (ix+0), a + ld (ix+1), e + ld (ix+2), a + ld (ix+3), e + inc a + inc a + djnz .mutefm + + ld (ix+0), $B4 ; Ensure all channels can be heard from both + ld (ix+1), $C0 ; speakers (by default they're mute!) + ld (ix+0), $B5 + ld (ix+1), $C0 + ld (ix+0), $B6 + ld (ix+1), $C0 + ld (ix+2), $B4 + ld (ix+3), $C0 + ld (ix+2), $B5 + ld (ix+3), $C0 + ld (ix+2), $B6 + ld (ix+3), $C0 + + ld (ix+0), $24 ; Init timers + ld (ix+1), $FE + ld (ix+0), $25 + ld (ix+1), $03 + ld (ix+0), $26 + ld (ix+0), $C9 + ld (ix+1), $FF + ld (ix+0), $27 + ld (ix+1), $3F + + jp IdleLoop ; Go into idle loop + +;**************************************************************************** +; PollPCM +; Used to update PCM while not idle +;**************************************************************************** + +PollPCM: macro + ld a, ($4000) + bit 0, a + call nz, UpdatePCM + endm + +;**************************************************************************** +; RunCommand +; Checks which command to run +;**************************************************************************** + +RunCommand: + dec a ; Command $01: load list + jp z, LoadList + dec a ; Command $02: play SFX + jp z, PlaySFX + dec a ; Command $03: stop SFX + jp z, StopSFXCmd + dec a ; Command $04: play BGM + jp z, PlayBGM + dec a ; Command $05: stop BGM + jp z, StopBGMCmd + + PollPCM + + xor a ; Bad command, ignore >:( + ld (RAM_Command), a + + PollPCM + +;**************************************************************************** +; IdleLoop +; Loop that runs when not processing SFX or BGM +;**************************************************************************** + +IdleLoop: + ld a, (RAM_Command) ; Look for commands + or a + jr nz, RunCommand + + PollPCM ; Poll PCM + + ld a, ($4000) ; Tick? + bit 1, a + jr nz, DoTick + bit 0, a ; Poll PCM again + call nz, UpdatePCM ; Not using macro for optimization purposes + + jp IdleLoop ; Keep idling + +;**************************************************************************** +; DoTick +; Called whenever a new tick triggers +;**************************************************************************** + +DoTick: + ld a, (ix+0) + bit 0, a + call nz, UpdatePCM + + ld (ix+0), $26 ; Reset timer +.timerset: + ld (ix+1), $C8 + ld (ix+0), $27 + ld (ix+1), $2F + + ld a, (.timerset+3) ; $C8 is too fast, $C9 is too slow + xor $01 ; So, we alternate between them to compensate + ld (.timerset+3), a + + PollPCM + +DoTick_SFX: ; Process SFXs + jp DoTick_SFXSkip +DoTick_SFXSkip: + + PollPCM + +DoTick_BGM: ; Process BGMs + jp DoTick_BGMSkip +DoTick_BGMSkip: + + PollPCM + + jp UpdatePSG ; Update PSG envelopes +DoTick_PSGSkip: + + PollPCM + + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; BankSwitch +; Switches into a new bank (won't update player status!) +; +; input A .... New bank to switch into +; input HL ... Must be $6000 +; breaks ..... AF +;**************************************************************************** + +BankSwitch: macro + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + rrca + ld (hl), a + ld (hl), l + rrca + ld (hl), a + endm + +;**************************************************************************** +; LoadList [command $01] +; Loads the pointer list +;**************************************************************************** + +LoadList: + ld hl, RAM_ComBank ; Get command parameters + ld c, (hl) + inc l + ld e, (hl) + inc l + ld d, (hl) + ex de, hl + + xor a ; Command parsed + ld (RAM_Command), a + + ld a, c ; Do initial bank switch + ld de, $6000 + ex de, hl + BankSwitch + ex de, hl + + ld de, RAM_PointerList ; Where the pointer list starts + +.loop: + ld a, (hl) ; Get high address byte + or a ; Is it the end of the list? + jp z, .end ; If so, stop parsing list + ld (de), a ; Nope, store it in list + + inc d ; Get address for next byte + inc l + jr nz, .noswitch1 + inc h + jr nz, .noswitch1 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch1: + + ld a, (hl) ; Get low address byte + ld (de), a ; Store it in pointer list + + inc d ; Get address for next byte + inc l + jr nz, .noswitch2 + inc h + jr nz, .noswitch2 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch2: + + ld a, (hl) ; Get bank byte + ld (de), a ; Store it in pointer list + + dec d ; Get address for next byte + dec d + inc e + inc l + jr nz, .noswitch3 + inc h + jr nz, .noswitch3 + inc c + ld a, c + ld h, $60 + BankSwitch + ld h, $80 +.noswitch3: + + jp .loop ; Go for next byte + +.end: + ld a, c ; Store current bank + ld (RAM_LastBank), a + jp IdleLoop ; End of subroutine + +;**************************************************************************** +; GetParam +; Subroutine for getting the parameter byte +;**************************************************************************** + +GetParam: + ld a, (RAM_LastBank) ; Bank switch? + cp c + jp z, .noswitchp + ld a, c + ld (RAM_LastBank), a + exx + BankSwitch + exx +.noswitchp: + ld b, (hl) ; Get volume + + PollPCM + + inc l ; Get next address + jp nz, .nonewbankp + inc h + jp nz, .nonewbankp + ld h, $80 + inc c +.nonewbankp: + + ret ; End of subroutine |
