; eXtended Genesis Music (XGM) Z80 driver - Stephane Dallongeville @2014-2016
;
; XGM is a music format dedicated to the Sega Megadrive/Genesis system.
; It has been designed to minimize CPU decoding resource and keep reasonable data size (should be smaller than VGM file).
; It supports both FM and PSG chip and allow up to 4 PCM channels (8 bits signed at 14 Khz) to be played at once.
; These 4 PCM channels are obtained by software mixing in the FM DAC in replacement of the 6th FM channel (so at best you can have 5FM + 4PCM + 4PSG = 13 channels)
;
; The driver supports playing SFX in PCM format with 16 priority levels.
; PCM samples can be >32KB but with the restriction of having their address and size aligned on 256 bytes.
;
; we have to do 254 cycles per sample output which consist of :
; - bufferize 4 PCM samples from the rom and mix them in write buffer
; - read 1 sample from read buffer and output it to the DAC
; - handle loop
; - bufferize XGM data / parse and apply XGM command / handle extern command
;
; register usage :
; HL  = sample source (in ROM)
; DE  = write buffer / XGM buffer
; BC  = counter
; C = $80 (used for overflow and sample unsign)
; IYH = temp value
; HL' = YMPORT1
; BC' = read buffer

; ###########################      define      ##############################

            INCLUDE "z80_def.i80"   ; basic definitions


; ###########################       var        ##############################

XGM_PLAY_SFT    EQU     6           ; XGM music play command bit
XGM_RESUME_SFT  EQU     5           ; XGM music resume command bit
XGM_PAUSE_SFT   EQU     4           ; XGM music pause command bit

                                    ; COMMAND b0 = play PCM0   STATUS b0 = PCM0 playing
                                    ; COMMAND b1 = play PCM1   STATUS b1 = PCM1 playing
                                    ; COMMAND b2 = play PCM2   STATUS b2 = PCM2 playing
                                    ; COMMAND b3 = play PCM3   STATUS b3 = PCM3 playing
                                    ; COMMAND b4 = stop XGM
                                    ; COMMAND b5 = resume XGM
                                    ; COMMAND b6 = play XGM    STATUS b6 = XGM playing

XGM_ARG_A   EQU     PARAMS+$00      ; XGM address (bit 0 --> bit 23)

PCM0_ARG_P  EQU     PARAMS+$04      ; priority (0 to 15)
PCM0_ARG_ID EQU     PARAMS+$05      ; PCM id (0 to 255)
PCM1_ARG_P  EQU     PARAMS+$06      ; priority (0 to 15)
PCM1_ARG_ID EQU     PARAMS+$07      ; PCM id (0 to 255)
PCM2_ARG_P  EQU     PARAMS+$08      ; priority (0 to 15)
PCM2_ARG_ID EQU     PARAMS+$09      ; PCM id (0 to 255)
PCM3_ARG_P  EQU     PARAMS+$0A      ; priority (0 to 15)
PCM3_ARG_ID EQU     PARAMS+$0B      ; PCM id (0 to 255)

PCM_ARG_P   EQU     PCM0_ARG_P
PCM_ARG_ID  EQU     PCM0_ARG_ID

LOOP_ARG    EQU     PARAMS+$0C      ; number of loop (0 = 255 loop)
PROTECT_ARG EQU     PARAMS+$0D      ; set to 1 from 68k during DMA (in vblank period)
                                    ; to prevent Z80 accessing 68k BUS

MODIFYING_F EQU     PARAMS+$0E      ; Z80 is modifying the 'PENDING_FRM' variable
PENDING_FRM EQU     PARAMS+$0F      ; contains number of XGM frame to process

PCM0_PRIO   EQU     PARAMS+$10      ; PCM0 internal priority
PCM0_ADDR   EQU     PARAMS+$12      ; PCM0 internal addr (bit8 --> bit23)
PCM0_LEN    EQU     PARAMS+$14      ; PCM0 internal len (bit8 --> bit23)
PCM1_PRIO   EQU     PARAMS+$18      ; PCM1 internal priority
PCM1_ADDR   EQU     PARAMS+$1A      ; PCM1 internal addr (bit8 --> bit23)
PCM1_LEN    EQU     PARAMS+$1C      ; PCM1 internal len (bit8 --> bit23)
PCM2_PRIO   EQU     PARAMS+$20      ; PCM2 internal priority
PCM2_ADDR   EQU     PARAMS+$22      ; PCM2 internal addr (bit8 --> bit23)
PCM2_LEN    EQU     PARAMS+$24      ; PCM2 internal len (bit8 --> bit23)
PCM3_PRIO   EQU     PARAMS+$28      ; PCM3 internal priority
PCM3_ADDR   EQU     PARAMS+$2A      ; PCM3 internal addr (bit8 --> bit23)
PCM3_LEN    EQU     PARAMS+$2C      ; PCM3 internal len (bit8 --> bit23)

PCM_PRIO    EQU     PCM0_PRIO
PCM_ADDR    EQU     PCM0_ADDR
PCM_LEN     EQU     PCM0_LEN

XGM_ADDR    EQU     PARAMS+$30      ; XGM internal addr (bit0 --> bit23)

WRITEBUF    EQU     PARAMS+$34      ; PCM write buffer pointer (bit8 --> bit9)
READBUF     EQU     PARAMS+$36      ; PCM read buffer pointer (bit8 --> bit9)

REM_LOOP    EQU     PARAMS+$38      ; remaining loop (0 = 255)

YM_RR_SAV   EQU     PARAMS+$40      ; YM RR save (4 slots * 6 channels)
PSG_ENV_SAV EQU     PARAMS+$58      ; PSG env save (4 channels)
YM_RR_OFF   EQU     PARAMS+$60      ; YM RR off (4 slots * 6 channels)
PSG_ENV_OFF EQU     PARAMS+$78      ; PSG env off (4 channels)

YM_2B_SAV   EQU     PARAMS+$5C      ; YM register $2B last write save
YM_2B_CNT   EQU     PARAMS+$5D      ; YM register $2B ON expiration

IDLE_LOOP   EQU     PARAMS+$7C      ; idle loop counter (XGM frame wait)
WAIT_LOOP   EQU     PARAMS+$7E      ; wait loop counter (DMA contention wait)

DEBUG_1     EQU     PARAMS+$80      ; debug
DEBUG_2     EQU     PARAMS+$81      ; debug
DEBUG_3     EQU     PARAMS+$82      ; debug
DEBUG_4     EQU     PARAMS+$83      ; debug
DEBUG_5     EQU     PARAMS+$84      ; debug
DEBUG_6     EQU     PARAMS+$85      ; debug
DEBUG_7     EQU     PARAMS+$86      ; debug
DEBUG_8     EQU     PARAMS+$87      ; debug
DEBUG_9     EQU     PARAMS+$88      ; debug
DEBUG_A     EQU     PARAMS+$89      ; debug
DEBUG_B     EQU     PARAMS+$8A      ; debug

ELAPSED     EQU     PARAMS+$90      ; elapsed frame since beginning of music (in frames), encoded on 24 bit

JUMP_TABLE  EQU     $1600           ; XGM command jump table (size = $100)
XGM_BUFFER  EQU     $1700           ; XGM music data buffer (size = $100)

PCM_BUFFER0 EQU     $1800           ; PCM buffer 0
PCM_BUFFER1 EQU     $1900           ; PCM buffer 1
PCM_BUFFER2 EQU     $1A00           ; PCM buffer 2
PCM_BUFFER3 EQU     $1B00           ; PCM buffer 3

PCM_BUFFER  EQU     PCM_BUFFER0     ; PCM buffer


ID_TABLE    EQU     $1C00           ; sample id table (size = $400)

STACK       EQU     JUMP_TABLE      ; stack pointer above buffer region ($1600 --> $15XX)


; ###########################      macro       ##############################

            INCLUDE "z80_mac.i80"  ; basic macros


; handlePCMCommand
; ----------------
; ? ->  HL  -> ?
; ? ->  BC  -> ?
; ? ->  SP  -> ?
;
; handle PCM command for channel 'ch' (PCM id < 0x100)
; = 157 cycles

            macro handlePCMCommand ch

            LD      BC, (PCM_ARG_P+(ch*2))  ; C = SFX prio, B = SFX id  ' 20    |
            LD      HL, PCM_PRIO+(ch*8)     ; HL point on PCM info      ' 10    | (30)

            LD      A, C                    ; A = new prio              ' 4     |
            CP      (HL)                    ; compare to old prio       ' 7     | 21 (51)
            JP      NC, .play_new           ; >= old prio --> play new  ' 10    |

            wait96                          ; sync                      ' 96    |
            JP      .end                    ;                           ' 10    | 106 (157)

.play_new
            LD      A, B                    ; A = SFX id                ' 4     |
            OR      A                       ; not a stop PCM command ?  ' 4     | 18 (69)
            JR      NZ, .PCM_play           ; go set new prio           ' 7/12  |

            LD      C, 0                    ; reset prio for stop       ' 7     | +2

.PCM_play                                   ;                           ' 69
            LD      (HL), C                 ; set new prio              ' 7     | (76)

            LD      H, (ID_TABLE>>10)       ;                           ' 7     |
            LD      L, A                    ;                           ' 4     |
            ADD     HL, HL                  ;                           ' 6     |
            ADD     HL, HL                  ; HL point on new PCM addr  ' 6     | 29 (105)
            LD      SP, HL                  ; set SP to new PCM addr    ' 6     |

            POP     HL                      ; copy params               ' 10    |
            LD      (PCM_ADDR+(ch*8)),HL    ;                           ' 16    |
            POP     HL                      ;                           ' 10    | 52 (157)
            LD      (PCM_LEN+(ch*8)),HL     ;                           ' 16    |

.end
            endm


; handlePCMCommandXGM
; -------------------
; XGM data ->  DE  -> XGM data
; PCM prio ->  C   -> ?
; ?        ->  HL  -> ?
; ?        ->  SP  -> ?
;
; handle PCM command for channel 'ch' for XGM music (PCM id < 0x40)
; = 142 cycles

            macro handlePCMCommandXGM ch

            LD      HL, PCM_PRIO+(ch*8)     ; HL point on PCM info      ' 10    |
            LD      A, C                    ; A = new prio              ' 4     |
            CP      (HL)                    ; compare new and old prio  ' 7     | (31)
            JP      NC, .play_new           ; >= old prio --> play new  ' 10    |

            INC     E                       ; next XGM command          ' 4     |
            wait97                          ; sync                      ' 97    | 111 (142)
            JP      .end                    ; done                      ' 10    |

.play_new
            LD      A, (DE)                 ; A = PCM id (max = $3F)    ' 7     |
            INC     E                       ; next XGM command          ' 4     |
            OR      A                       ; not a stop PCM command ?  ' 4     | 27 (58)
            JR      NZ, .PCM_play           ; go set new prio           ' 7/12  |

            LD      C, 0                    ; reset prio for stop       ' 7     | +2

.PCM_play
            LD      (HL), C                 ; set new prio              ' 7     | (65)

            LD      H, (ID_TABLE>>8)        ;                           ' 7     |
            ADD     A                       ;                           ' 4     |
            ADD     A                       ;                           ' 4     | 25 (90)
            LD      L, A                    ; HL point on new PCM addr  ' 4     |
            LD      SP, HL                  ; set SP to new PCM addr    ' 6     |

            POP     HL                      ; copy params               ' 10    |
            LD      (PCM_ADDR+(ch*8)),HL    ;                           ' 16    |
            POP     HL                      ;                           ' 10    | 52 (142)
            LD      (PCM_LEN+(ch*8)),HL     ;                           ' 16    |

.end                                        ;                           ' 142

            endm


; prepareChannelAlt
; -----------------
; ? ->  HL  ->  point to the sample source (ROM)
; ? ->  DE  ->  point to write buffer
;
; set bank and prepare registers to play PCM channel 'ch'
; = 176 cycles

            macro prepareChannelAlt ch

            LD      HL, (PCM_ADDR+(ch*8))   ; HL = sample addr (HM) ' 16    | (16)

            LD      A, L                    ; A = bit 8-15          ' 4     |
            RLA                             ; C flag = bit 15       ' 4     |
            LD      A, H                    ; A = bit 16-23         ' 4     | 16 (32)
            RLA                             ; A = bit 15-22         ' 4     |

            setBank_DE                      ; set bank              ' 105   | (137)

            LD      H, L                    ;                       ' 4     |
            SET     7, H                    ; HL |= 0x8000          ' 8     | 19 (156)
            LD      L, 0                    ; HL = sample addr bank ' 7     |

            LD      DE, (WRITEBUF)          ; DE = write buffer     ' 20    | (176)

            endm

; prepareChannel
; --------------
; ? ->  SP  ->  point to the sample source (ROM)
; ? ->  HL  ->  point to write buffer
;
; set bank and prepare registers to play PCM channel 'ch'
; = 178 cycles

            macro prepareChannel ch

            LD      HL, (PCM_ADDR+(ch*8))   ; HL = sample addr (HM) ' 16    | (16)

            LD      A, L                    ; A = bit 8-15          ' 4     |
            RLA                             ; C flag = bit 15       ' 4     |
            LD      A, H                    ; A = bit 16-23         ' 4     | 16 (32)
            RLA                             ; A = bit 15-22         ' 4     |

            setBank_DE                      ; set bank              ' 105   | (137)

            LD      H, L                    ;                       ' 4     |
            SET     7, H                    ; HL |= 0x8000          ' 8     | 25 (162)
            LD      L, 0                    ; HL = sample addr bank ' 7     |
            LD      SP, HL                  ; SP point on sample    ' 6     |

            LD      HL, (WRITEBUF)          ; HL = write buffer     ' 16    | (178)

            endm


; stopChannel
; -----------
; ? ->  HL  -> STATUS
;
; set null sample play for channel 'ch'
; = 75 cycles

            macro stopChannel ch

            LD      HL, (ID_TABLE+0)        ; HL = null PCM addr        ' 16    |
            LD      (PCM_ADDR+(ch*8)), HL   ; set new PCM addr          ' 16    |
            LD      HL, $0001               ; HL = null PCM len         ' 10    | (58)
            LD      (PCM_LEN+(ch*8)), HL    ; set new PCM addr          ' 16    |

            XOR     A                       ;                           ' 4     |
            LD      (PCM_PRIO+(ch*8)), A    ; clear prio                ' 13    | 17 (75)

            endm


; updateChannelData
; -----------------
; ? ->  HL  -> ?
;
; update sample address and sample lenght
; check if sample is done and stop it if needed
; = 153 cycles

            macro updateChannelData ch

            LD      HL, (PCM_ADDR+(ch*8))   ; increment address     ' 16    |
            INC     HL                      ;                       ' 6     | (38)
            LD      (PCM_ADDR+(ch*8)), HL   ;                       ' 16    |

            LD      HL, (PCM_LEN+(ch*8))    ; decrement lenght      ' 16    |
            DEC     HL                      ;                       ' 6     | 22 (60)

            LD      A, H                    ;                       ' 4     |
            OR      L                       ;                       ' 4     | 18 (78)
            JP      Z, .done                ; sample done ?         ' 10    |

            LD      (PCM_LEN+(ch*8)), HL    ; set new sample len    ' 16    |
            wait49                          ;                       ' 49    | 75 (153)
            JP      .end                    ;                       ' 10    |

.done
            stopChannel ch                  ; stop channel          ' 75    | (153)

.end

            endm


; compareReadWrite
; ----------------
; read buffer  -> BC'
; write buffer -> DE
;                 ZF ->  Write == Read buffer
;
; compare read and write buffer
; = 16 cycles

            macro compareReadWrite

            EXX                     ;                           ' 4     |
            LD      A, B            ; A = read buffer high      ' 4     |
            EXX                     ;                           ' 4     | 16
            CP      D               ; compare write buffer high ' 4     |

            endm


; readAndClear
; ------------
; point to sample source (ROM) ->  HL  ->  point to sample source (ROM)
; point to write buffer        ->  DE  ->  point to write buffer
; ?                            ->  PF  ->  0=done 1=not done
;
; read 1 sample from rom and write it in output buffer
; = 19 cycles

            macro readAndClear

            LDI                     ;                           ' 16+3  | 19

            endm


; readAndClear2
; -------------
; point to sample source (ROM) ->  HL  ->  point to sample source (ROM)
; point to write buffer        ->  DE  ->  point to write buffer
; ?                            ->  PF  ->  0=done 1=not done
;
; read 2 samples from rom and write them in write buffer
; = 38 cycles

            macro readAndClear2

            LDI                     ;                           ' 16+3  |
            LDI                     ;                           ' 16+3  | (38)

            endm


; readAndMix2
; -----------
; point to sample source (ROM) ->  SP  ->  point to sample source (ROM)
; point to write buffer        ->  HL  ->  point to write buffer
; $80 ->  C
;
; read 2 samples and mix them in output buffer
; = 80 (+22 when overflow)

            macro readAndMix2

            POP     DE              ; read 2 samples from ROM   ' 10+6  | (16)

            LD      A, E            ; first sample              ' 4     |
            ADD     (HL)            ; mix with write buffer     ' 7     | 21 (37)
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok
            LD      (HL), A         ; store it in write sample  ' 7     |
            INC     L               ;                           ' 4     | 11 (48)

            LD      A, D            ; second sample             ' 4     |
            ADD     (HL)            ; mix                       ' 7     | 21 (69)
            JP      PO, .ok2        ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok2
            LD      (HL), A         ; store it in write sample  ' 7     |
            INC     L               ;                           ' 4     | 11 (80)

            endm                    ;                           ' 80 (+22)


; readAndMix16WhilePlay3
; ----------------------
; point to sample source (ROM) ->  SP  ->  point to sample source (ROM)
; point to write buffer        ->  HL  ->  point to write buffer
; $80 ->  C
;
; read 16 samples and mix them in output buffer
; = 2 samples + 240 cycles

            macro readAndMix16WhilePlay3

            sampleOutput            ;                           ' 36    | (36)

            readAndMix2             ; read and mix 2 samples    ' 80    |
            readAndMix2             ; read and mix 2 samples    ' 80    | 160 (196)

            POP     DE              ; read 2 samples from ROM   ' 10+6  | (212)

            LD      A, E            ; first sample              ' 4     |
            ADD     (HL)            ; mix with write buffer     ' 7     | 21 (233)
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok
            LD      (HL), A         ; store it in write sample  ' 7     |
            INC     L               ;                           ' 4     | 11 (254-10)

            sampleOutput            ; -10 --> too soon          ' 36    | (36-10)

            LD      A, D            ; second sample             ' 4     |
            ADD     (HL)            ; mix                       ' 7     | 21 (47)
            JP      PO, .ok2        ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok2
            LD      (HL), A         ; store it in write sample  ' 7     |
            INC     L               ;                           ' 4     | 11 (58)

            readAndMix2             ; read and mix 2 samples    ' 80    |
            readAndMix2             ; read and mix 2 samples    ' 80    | 160 (218)

            POP     DE              ; read 2 samples from ROM   ' 10+6  | (234)

            LD      A, E            ; first sample              ' 4     |
            ADD     (HL)            ; mix with write buffer     ' 7     | 21 (254+1)
            JP      PO, .ok3        ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok3
            LD      (HL), A         ; store it in write sample  ' 7     | (254+8)

            sampleOutput            ; +8 --> too late           ' 36    | (36+8)

            INC     L               ;                           ' 4     | (48)

            LD      A, D            ; second sample             ' 4     |
            ADD     (HL)            ; mix                       ' 7     | 21 (69)
            JP      PO, .ok4        ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok4
            LD      (HL), A         ; store it in write sample  ' 7     |
            INC     L               ;                           ' 4     | 11 (80)

            readAndMix2             ; read and mix 2 samples    ' 80    |
            readAndMix2             ; read and mix 2 samples    ' 80    | 160 (240)

            endm                    ;                           ' 240


; readMixAndUnsign
; ----------------
; point to sample source (ROM) ->  HL  ->  point to sample source (ROM)
; point to write buffer        ->  DE  ->  point to write buffer
; $80 ->  C
;
; read 1 sample and mix it with output buffer, then unsign it
; = 46 (+11 when overflow)

            macro readMixAndUnsign

            LD      A, (DE)         ; read value in write buf   ' 7     |
            ADD     (HL)            ; mix with source sample    ' 7+3   | (27)
            JP      PO, .ok         ; check overflow            ' 10    |

            LD      A, C            ; fix overflow              ' 4     |
            ADC     $FF             ; A = $7F/$80               ' 7     | +11

.ok
            ADD     C               ; unsign                    ' 4     |
            LD      (DE), A         ; write sample in buffer    ' 7     | 19 (46)
            INC     E               ;                           ' 4     |
            INC     L               ; next                      ' 4     |

            endm                    ;                           ' 46 (+11)


; sampleOutput
; ------------
; YMPORT0     -> HL'
; YMPORT1     -> DE'
; read buffer -> BC' ->  read buffer
;
; output a sample to the DAC
; = 36 cycles

            macro sampleOutput

            EXX                     ;                           ' 4     | 4

            LD      A, (BC)         ; read sample from buffer   ' 7     |
            INC     BC              ; increment read address    ' 6     |
            RES     2, B            ; read_address &= 0x03FF    ' 8     | 28 (32)
            LD      (DE), A         ; play sample               ' 7     |

            EXX                     ;                           ' 4     | (36)

            endm


; sampleOutputSafe
; ----------------
; YMPORT0     -> HL'
; YMPORT1     -> DE'
; read buffer -> BC' ->  read buffer
;
; output a sample to the DAC
; = 46 cycles

            macro sampleOutputSafe

            EXX                     ;                           ' 4     | 4

            LD      (HL), $2A       ; prepare DAC write         ' 10    |
            LD      A, (BC)         ; read sample from buffer   ' 7     |
            INC     BC              ; increment read address    ' 6     | 38 (42)
            RES     2, B            ; read_address &= 0x03FF    ' 8     |
            LD      (DE), A         ; play sample               ' 7     |

            EXX                     ;                           ' 4     | (46)

            endm


; waitYMReadyFast
; ---------------
; YMPORT0   -> HL
;
; wait until YM ready
; = 22 cycles minimum (22 * X)

            macro waitYMReadyFast

.wait
            BIT     7, (HL)         ; test YM ready bit         ' 12    | (22)
            JP      NZ, .wait       ; wait while busy           ' 10    |

            endm


; waitYMReady
; -----------
; YMPORT0   -> HL'
;
; wait until YM ready
; = 30 cycles minimum (8 + (22 * X))

            macro waitYMReady

            EXX                     ;                           ' 4     | (4)

.wait
            BIT     7, (HL)         ; test YM ready bit         ' 12    | (22)
            JP      NZ, .wait       ; wait while busy           ' 10    |

            EXX                     ;                           ' 4     | (30)

            endm


; ###########################       init       ##############################

            ORG     $0000

init
            DI                      ; disable ints
            LD      SP, STACK       ; setup stack
            IM      $01             ; set int mode 1
            XOR     A
            LD      (STATUS), A     ; driver not ready
            LD      (COMMAND), A    ; command cleared
            JP      start           ; jump to start


; ###########################       main       ##############################

            BLOCK   $0200-$

start

            LD      HL, PARAMS
            LD      A, $00
            LD      B, $40

cp_loop
            LD      (HL), A         ; clear parameters
            INC     HL
            DJNZ    cp_loop

            LD      HL, YM_RR_OFF
            LD      A, $FF
            LD      B, (6 * 4)

off_loop
            LD      (HL), A         ; clear off settings
            INC     HL
            DJNZ    off_loop

            LD      HL, PSG_ENV_OFF

            LD      (HL), $9F       ; PSG channel 0 off
            INC     HL
            LD      (HL), $BF       ; PSG channel 1 off
            INC     HL
            LD      (HL), $DF       ; PSG channel 2 off
            INC     HL
            LD      (HL), $FF       ; PSG channel 3 off

            LD      HL, YM_RR_OFF
            LD      DE, YM_RR_SAV
            LD      BC, (6 * 4) + 4
            LDIR                    ; copy off settings to sav settings

            LD      HL, PCM_BUFFER
            LD      A, $80
            LD      B, $00          ; for 256 * 4 bytes to clear

cb_loop
            LD      (HL), A         ; initialise buffers to silent
            INC     HL
            LD      (HL), A
            INC     HL
            LD      (HL), A
            INC     HL
            LD      (HL), A
            INC     HL
            DJNZ    cb_loop

            LD      A, $00

            LD      HL, YM_2B_SAV
            LD      (HL), A         ; DAC disabled by default
            LD      HL, YM_2B_CNT
            LD      (HL), A         ; DAC disabled by default

            LD      HL, MODIFYING_F
            LD      (HL), A         ; clear modifying variable flag
            LD      HL, PENDING_FRM
            LD      (HL), A         ; clear frame to process counter

            CALL    initDAC         ; prepare DAC for output

            LD      HL, PCM_BUFFER0 ; initialise write and read buffer
            LD      (WRITEBUF), HL
            LD      HL, PCM_BUFFER1
            LD      (READBUF), HL

            LD      BC, HL          ; BC' point to read buffer
            LD      HL, YMPORT0     ; HL' point to YMPORT0
            LD      DE, YMPORT1     ; DE' point to YMPORT1
            EXX

            stopChannel 0           ; stop all channels
            stopChannel 1
            stopChannel 2
            stopChannel 3

            LD      A, STATREADY
            LD      (STATUS), A     ; driver ready

main_loop

;    LD  A, (VCOUNTER)
;    LD  (DEBUG_1), A


; prepare XGM buffer
; ------------------

; $00
            sampleOutput                ;                       ' 36    | 36

            LD      DE, XGM_BUFFER      ; DE point to XGM buf   ' 10    |
            LD      A, (STATUS)         ; A = STATUS            ' 13    |
            BIT     XGM_PLAY_SFT, A     ; is XGM playing ?      ' 8     | 41 (77)
            JP      NZ, has_xgm         ; go to prepare buf     ' 10    |

            wait146                     ; sync                  ' 146   |
            XOR     A                   ; A = 0                 ' 4     | 160 (237)
            JP      end_prep_xgm        ; preparation done      ' 10    |

has_xgm
            LD      HL, (XGM_ADDR)      ; XGM addr (b0-b15)     ' 16    |
            LD      A, H                ; A = XGM addr (b8-b15) ' 4     |
            RLA                         ; CF = XGM addr b15     ' 4     | 146 (223)
            LD      A, (XGM_ADDR+2)     ; A = sample addr (H)   ' 13    |
            RLA                         ; sample addr (b22-b15) ' 4     |
            setBank_BC                  ; setBank               ' 105   |

            LD      A, H                ; save H                ' 4     |
            SET     7, H                ; HL = XGM addr bank    ' 8     |
            LD      B, $0               ; B = 0                 ' 7     | 30 (254-1)
            LD      C, (HL)             ; BC = bytes to process ' 7     |
            LD      H, A                ; restore H             ' 4     |

; $00+X
            sampleOutput                ;                       ' 36    | (36-1)

            LD      A, L                ; A = addr low          ' 4     |
            ADD     C                   ; compute new addr (L)  ' 4     |
            LD      (XGM_ADDR+0), A     ; store new addr (L)    ' 13    | 28 (63)
            JR      C, .split           ; carry --> separate    ' 7     |

.no_split
            wait104                     ; sync                  ' 104   | (167)

            SET     7, H                ; HL = XGM addr bank    ' 8     |
            INC     HL                  ; HL = start frame data ' 6     |
            XOR     A                   ; no second part        ' 4     |
            LD      IYH, A              ; IYH = sec part size   ' 8     | 44 (211)
            NOP                         ;                       ' 4     |
            DEC     C                   ; first part size = 0 ? ' 4     |
            JP      Z, .second_part     ; go to second part     ' 10    |

            wait12                      ; sync                  ' 12    | 22 (233)
            JP      .first_part_pre     ; start filling         ' 10    |

.split
            LD      IYH, A              ; IYH = sec part size   ' 8     |
            SUB     C                   ;                       ' 4     |
            NEG                         ;                       ' 8     | 34 (97)
            DEC     A                   ; A = (C - IYH) - 1     ' 4     |
            JP      NZ, .first_part_ok  ; not zero --> do it    ' 10    |

            LD      A, H                ; A = addr med (M)      ' 4     |
            ADD     $1                  ; inc with CF set       ' 7     |
            LD      (XGM_ADDR+1), A     ; store new addr (M)    ' 13    |
            LD      A, (XGM_ADDR+2)     ; load sample addr (H)  ' 13    |
            ADC     B                   ; inc                   ' 4     | 68 (164)
            LD      (XGM_ADDR+2), A     ; store sample addr (H) ' 13    |
            SET     7, H                ; HL = XGM addr bank    ' 8     |
            INC     HL                  ; HL = start frame data ' 6     |

            wait36                      ; sync                  ' 36    |
            JP      .second_part        ; go second part        ' 10    | 65 (211)

.first_part_ok
            LD      C, A                ; C = first part size   ' 4     |
            LD      A, H                ; A = addr med (M)      ' 4     |
            ADD     $1                  ; inc with CF set       ' 7     |
            LD      (XGM_ADDR+1), A     ; store new addr (M)    ' 13    |
            LD      A, (XGM_ADDR+2)     ; load sample addr (H)  ' 13    | 72 (169)
            ADC     B                   ; inc                   ' 4     |
            LD      (XGM_ADDR+2), A     ; store sample addr (H) ' 13    |
            SET     7, H                ; HL = XGM addr bank    ' 8     |
            INC     HL                  ; HL = start frame data ' 6     |

            wait64                      ; sync                  ' 64    | (233)

.first_part_pre                         ;                       ' 233
            LD      A, 11               ; A = block fill size   ' 7     |
            CP      C                   ; C < block size        ' 4     | 21 (254)
            JP      NC, .first_part_end ; go to last bytes      ' 10    |

.first_part
; $00+X
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            LDI                         ; 11 bytes at once      ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  | 187 (223)
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |

            wait10                      ; sync                  ' 10    |
            LD      A, 11               ; A = block fill size   ' 7     |
            CP      C                   ; remaining > 11        ' 4     | 31 (254)
            JP      C, .first_part      ; continu block fill    ' 10    |

.first_part_end
; $00+X
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            JP      PO, .fst_part_d0    ;                       ' 10    | 27 (63)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d1    ;                       ' 10    | 27 (90)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d2    ;                       ' 10    | 27 (117)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d3    ;                       ' 10    | 27 (144)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d4    ;                       ' 10    | 27 (171)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d5    ;                       ' 10    | 27 (198)

            wait56                      ; sync                  ' 56    | (254)

; $00+X
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            JP      PO, .fst_part_d0    ;                       ' 10    | 27 (63)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d1    ;                       ' 10    | 27 (90)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d2    ;                       ' 10    | 27 (117)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d3    ;                       ' 10    | 27 (144)
            LDI                         ;                       ' 16+1  |
            JP      PO, .fst_part_d4    ;                       ' 10    | 27 (171)
            LDI                         ;                       ' 16+1  |
            JP      .fst_part_d5        ;                       ' 10    | 27 (198)

.fst_part_d0
            wait27                      ; sync                  ' 27    | (90)
.fst_part_d1
            wait27                      ; sync                  ' 27    | (117)
.fst_part_d2
            wait27                      ; sync                  ' 27    | (144)
.fst_part_d3
            wait27                      ; sync                  ' 27    | (171)
.fst_part_d4
            wait27                      ; sync                  ' 27    | (198)
.fst_part_d5
            wait13                      ; sync                  ' 13    | (211)

.second_part                            ;                       ' 211
            NOP                         ; sync                  ' 4
            LD      A, IYH              ; A = second part size  ' 8     |
            OR      A                   ;                       ' 4     | 26 (237)
            JP      Z, end_prep_xgm     ; done                  ' 10    |

            wait17                      ; sync                  ' 17    | 254

; $00+X
            sampleOutput                ;                       ' 36    | 36

            LD      HL, (XGM_ADDR)      ; XGM addr (b0-b15)     ' 16    |
            LD      A, H                ; A = XGM addr (b8-b15) ' 4     |
            RLA                         ; CF = XGM addr b15     ' 4     | 146 (182)
            LD      A, (XGM_ADDR+2)     ; A = sample addr (H)   ' 13    |
            RLA                         ; sample addr (b22-b15) ' 4     |
            setBank_BC                  ; setBank               ' 105   |

            SET     7, H                ; HL = XGM addr bank    ' 8     | 15 (197)
            LD      L, 0                ;                       ' 7     |

            wait21                      ; sync                  ' 21    | (218)

            LD      B, 0                ; B = 0                 ' 7     |
            LD      C, IYH              ; BC = second part size ' 8     |
            LD      A, 11               ; A = block fill size   ' 7     | 36 (254)
            CP      C                   ; C < block size        ' 4     |
            JP      NC, .sec_part_end   ; go to last bytes      ' 10    |

; $00+X
.sec_part_loop
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            LDI                         ; 11 bytes at once      ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  | 187 (223)
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |
            LDI                         ;                       ' 16+1  |

            wait10                      ; sync                  ' 10    |
            LD      A, 11               ; A = block fill size   ' 7     |
            CP      C                   ; remaining > 11        ' 4     | 31 (254)
            JP      C, .sec_part_loop   ; continu block fill    ' 10    |

.sec_part_end
; $00+X
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            JP      PO, .sec_part_d0    ;                       ' 10    | 27 (63)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d1    ;                       ' 10    | 27 (90)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d2    ;                       ' 10    | 27 (117)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d3    ;                       ' 10    | 27 (144)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d4    ;                       ' 10    | 27 (171)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d5    ;                       ' 10    | 27 (198)

            wait56                      ; sync                  ' 56    | (254)

; $00+X
            sampleOutput                ;                       ' 36    | 36

            LDI                         ; fill XGM buffer       ' 16+1  |
            JP      PO, .sec_part_d0    ;                       ' 10    | 27 (63)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d1    ;                       ' 10    | 27 (90)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d2    ;                       ' 10    | 27 (117)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d3    ;                       ' 10    | 27 (144)
            LDI                         ;                       ' 16+1  |
            JP      PO, .sec_part_d4    ;                       ' 10    | 27 (171)
            LDI                         ;                       ' 16+1  |
            JP      .sec_part_d5        ;                       ' 10    | 27 (198)

.sec_part_d0
            wait27                      ; sync                  '       | (90)
.sec_part_d1
            wait27                      ; sync                  '       | (117)
.sec_part_d2
            wait27                      ; sync                  '       | (144)
.sec_part_d3
            wait27                      ; sync                  '       | (171)
.sec_part_d4
            wait27                      ; sync                  '       | (198)
.sec_part_d5
            wait35                      ; sync                  ' 35    |
            XOR     A                   ; A = 0                 ' 4     | 39 (237)

end_prep_xgm                            ;                       ' 237
            LD      (DE), A             ; XGM buf end with 0    ' 7     |
            wait10                      ; sync                  ' 10    | 17 (254)


; XGM PREP DONE
; -------------

; $01+x
            sampleOutput                ;                       ' 36    | (36)

            LD      A, (STATUS)         ; A = STATUS            ' 13    |
            BIT     XGM_PLAY_SFT, A     ; XGM playing ?         ' 8     | 31 (67)
            JP      NZ, .inc            ; count elapsed         ' 10    |

            wait71                      ; sync                  ' 71    | 81 (148)
            JP      .noinc              ; preparation done      ' 10    |

.inc
            LD      HL, ELAPSED         ;                       ' 10    | (77)

            LD      A, (HL)             ;                       ' 7     |
            ADD     A, #1               ;                       ' 7     |
            LD      (HL), A             ;                       ' 7     |
            INC     L                   ;                       ' 4     |
            LD      A, (HL)             ;                       ' 7     |
            ADC     A, #0               ; inc elapsed frame     ' 7     | 71 (148)
            LD      (HL), A             ; (24 bit counter)      ' 7     |
            INC     L                   ;                       ' 4     |
            LD      A, (HL)             ;                       ' 7     |
            ADC     A, #0               ;                       ' 7     |
            LD      (HL), A             ;                       ' 7     |

.noinc
            wait60                      ; sync                  ' 60    | (208)

            LD      DE, (WRITEBUF)      ; DE = write buf        ' 20    |
            compareReadWrite            ; PCM buffer full ?     ' 16    | 46 (254)
            JP      Z, sync_frame       ; go to sync directly   ' 10    |

pcm_mix

; PCM channel 0
; -------------

; $02+X
            sampleOutput                ;                       ' 36    |
            prepareChannelAlt 0         ;                       ' 176   |
            LD      BC, 242             ; prepare loop counter  ' 10    | 254
            wait32                      ; sync                  ' 32    |

; $03-$18+X
.loop_ch0
            sampleOutput                ;                       ' 36    |
            readAndClear2               ;                       ' 38    |
            readAndClear2               ; process 11 samples    ' 38    |
            readAndClear2               ;                       ' 38    | 254-1
            readAndClear2               ;                       ' 38    |
            readAndClear2               ;                       ' 38    |
            readAndClear                ;                       ' 19    |
            wait8                       ; sync                  ' 8     |

            sampleOutput                ; -1 --> a bit early    ' 36    |
            readAndClear2               ;                       ' 38    |
            readAndClear2               ; process 11 samples    ' 38    |
            readAndClear2               ;                       ' 38    | 254+1
            readAndClear2               ;                       ' 38    |
            readAndClear2               ;                       ' 38    |
            readAndClear                ;                       ' 19    |
            JP      PE, .loop_ch0       ;                       ' 10    |

; $19+X
            sampleOutput                ;                       ' 36    |
            readAndClear2               ;                       ' 38    |
            readAndClear2               ; process 10 samples    ' 38    |
            readAndClear2               ;                       ' 38    | 254+10
            readAndClear2               ;                       ' 38    |
            readAndClear2               ;                       ' 38    |
            readAndClear2               ;                       ' 38    |

; $1A+X
            sampleOutput                ; +10 --> too late      ' 36+10 |
            readAndClear2               ;                       ' 38    |
            updateChannelData 0         ; update channel data   ' 153   | 254
            wait17                      ; sync                  ' 17    |


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_2), A

; PCM channel 1
; -------------

; $1B+X
            sampleOutput                ;                       ' 36    |
            prepareChannel 1            ;                       ' 178   | 254
            LD      BC, $1080           ; prepare loop counter  ' 10    |
            wait30                      ; sync                  ' 30    |

; $1C-4B+X
.loop_ch1
            readAndMix16WhilePlay3      ;                       ' 240   |
            DEC     B                   ;                       ' 4     | 254*3
            JP      NZ, .loop_ch1       ;                       ' 10    |

; $4C+X
            sampleOutput                ;                       ' 36    |
            updateChannelData 1         ; update channel data   ' 153   | 254
            wait65                      ; sync                  ' 65    |


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_3), A

; PCM channel 2
; -------------

; $4D+X
            sampleOutput                ;                       ' 36    |
            prepareChannel 2            ;                       ' 178   | 254
            LD      BC, $1080           ; prepare loop counter  ' 10    |
            wait30                      ; sync                  ' 30    |

; $4E-7D+X
.loop_ch2
            readAndMix16WhilePlay3      ;                       ' 240   |
            DEC     B                   ;                       ' 4     | 254*3
            JP      NZ, .loop_ch2       ;                       ' 10    |

; $7E+X
            sampleOutput                ;                       ' 36    |
            updateChannelData 2         ; update channel data   ' 153   | 254
            wait65                      ; sync                  ' 65    |


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_4), A

; PCM channel 3
; -------------

; $7F+X
            sampleOutput                ;                       ' 36    |
            prepareChannelAlt 3         ;                       ' 176   | 254
            LD      BC, $1C80           ; prepare loop counter  ' 10    |
            wait32                      ; sync                  ' 32    |

; $80-$B7+X
.loop_ch3
            sampleOutput                ;                       ' 36    |
            readMixAndUnsign            ; mix/unsign 9 samples  ' 46    |
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    | 254+12
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    |

            sampleOutput                ; +12 --> late          ' 36    |
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    | 254
            readMixAndUnsign            ;                       ' 46    |
            wait8                       ; sync                  ' 8     |
            DEC     B                   ;                       ' 4     |
            JP      NZ, .loop_ch3       ;                       ' 10    |

; $B8+X
            sampleOutput                ;                       ' 36    |
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    | 254
            readMixAndUnsign            ;                       ' 46    |
            readMixAndUnsign            ;                       ' 46    |
            wait34                      ;                       ' 34    |

; $B9+X
            sampleOutput                ;                       ' 36    |
            updateChannelData 3         ; update channel data   ' 153   | 254
            wait65                      ; sync                  ' 65    |


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_5), A

; next write buffer
; -----------------

; $BA+X
next_write_buf
            sampleOutput                ;                       ' 36    | 36

            LD      A, (STATUS)         ; A = (STATUS)          ' 13    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      (HL), $2B           ; register = DAC enable ' 10    | 54 (90)
            INC     L                   ; HL point on YM port1  ' 4     |
            AND     $0F                 ; PCM is playing ?      ' 7     |
            JP      NZ, .still_pcm      ;                       ' 10    |

            LD      A, (YM_2B_CNT)      ;                       ' 13    |
            OR      A                   ;                       ' 4     | 27 (117)
            JP      Z, .no_pcm          ; DAC enabled expired ? ' 10    |

            DEC     A                   ; decrement expiration  ' 4     |
            LD      (YM_2B_CNT), A      ;                       ' 13    |
            LD      A, $80              ; DAC enabled           ' 7     | 34 (151)
            JP      .set_dac            ; assume still PCM      ' 10    |

.still_pcm                              ;                       ' 90
            wait7                       ; sync                  ' 7     |
            LD      A, 3                ; set DAC ON expiration ' 7     | 27 (117)
            LD      (YM_2B_CNT), A      ; 4 frames to expire    ' 13    |

            wait17                      ; sync                  ' 17    |
            LD      A, $80              ; DAC enabled           ' 7     | 34 (151)
            JP      .set_dac            ; assume still PCM      ' 10    |

.no_pcm                                 ;                       ' 117
            wait21                      ; sync                  ' 21    |
            LD      A, (YM_2B_SAV)      ; use current DAC on    ' 13    | 34 (151)

.set_dac                                ;                       ' 151
            LD      (HL), A             ; set DAC enabled       ' 7     |
            DEC     L                   ; HL point on YM port0  ' 4     | 11 (162)

            EXX                         ;                       ' 4     |
            LD      A, B                ; A = read buffer high  ' 4     |
            LD      (READBUF+1), A      ; save current read buf ' 13    | 25 (187)
            EXX                         ;                       ' 4     |

            CP      D                   ; prepare DAC write     ' 4     |
            LD      (HL), 0x2A          ; write buf == read buf ' 10    | 24 (211)
            JP      Z, .continu_pcm     ; continu to fill pcm   ' 10    |

            INC     D                   ; inc write addr        ' 4     |
            RES     2, D                ; write addr &= 0x03FF  ' 8     | 42 (254-1)
            LD      (WRITEBUF), DE      ; save new write addr   ' 20    |
            JP      sync_frame          ;                       ' 10    |

.continu_pcm
            INC     D                   ; inc write addr        ' 4     |
            RES     2, D                ; write addr &= 0x03FF  ' 8     | 42 (254-1)
            LD      (WRITEBUF), DE      ; save new write addr   ' 20    |
            JP      pcm_mix             ; do pcm mix again      ' 10    |

; sync
; ----

; $BB+X
sync_frame                              ; DE point on write buf

;    LD  A, (VCOUNTER)
;    LD  (DEBUG_6), A

sync_frame_loop
            sampleOutput                ;                       ' 36    | (36)

            wait62                      ; sync                  ' 62    | (98)

            LD      HL, (IDLE_LOOP)     ;                       ' 16    |
            INC     HL                  ; increment idle loop   ' 6     | 38 (136)
            LD      (IDLE_LOOP), HL     ;                       ' 16    |

            LD      A, (PENDING_FRM)    ; A = frame to process  ' 13    |
            OR      A                   ; something to do ?     ' 4     | 27 (163)
            JP      NZ, .do_xgm         ; do the XGM frame      ' 10    |

            LD      A, (PROTECT_ARG)    ; get BUS protect state ' 13    |
            OR      A                   ; protecting ?          ' 4     | 27 (190)
            JP      NZ, .wait_dma       ; wait for it           ' 10    |

            EXX                         ;                       ' 4     |
            LD      A, B                ; A = read buffer high  ' 4     |
            LD      (READBUF+1), A      ; save current read buf ' 13    | 25 (215)
            EXX                         ;                       ' 4     |

            ADD     $1                  ;                       ' 7     |
            RES     2, A                ; read pos &= 0x03FF    ' 8     |
            CP      D                   ; pcm buffer empty ?    ' 4     | 29 (244)
            JP      Z, .do_pcm          ; fill pcm              ' 10    |

            JP      sync_frame_loop     ; wait for a frame      ' 10    | (254)

.wait_dma                               ;                       ' 190
            LD      HL, (WAIT_LOOP)     ;                       ' 16    |
            INC     HL                  ; increment wait loop   ' 6     | 38 (228)
            LD      (WAIT_LOOP), HL     ;                       ' 16    |

            wait16                      ; sync                  ' 16    |
            JP      sync_frame          ; wait for a frame      ' 10    | 26 (254)

.do_pcm                                 ;                       ' 244
            JP      pcm_mix             ; do pcm mix again      ' 10    | (254)

.do_xgm                                 ;                       ' 163
            wait81                      ; sync                  ' 81    |
            LD      DE, XGM_BUFFER      ; DE point to XGM buf   ' 10    | 91 (254)


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_7), A

; execute XGM command
; -------------------

; $BC+X+Y
execute_xgm
            sampleOutput                ;                       ' 36    | (36)

            LD      A, (DE)             ; A = command           ' 7     |
            LD      (.ld_hl_xx + 1), A  ; set jump address      ' 13    |
            INC     E                   ; next command          ' 4     | 44 (80)
.ld_hl_xx                               ;                       '       |
            LD      HL, (JUMP_TABLE)    ; LD HL, (jt)           ' 16    |
            JP      (HL)                ;                       ' 4     |


; next frame command
; ------------------

com_next_frame                          ; 00                    ' 80
            JP      xgm_done            ; XGM frame done        ' 10    | (90)


; PSG command
; -----------

com_psg_tone_w0                         ; 10                    ' 80
            wait126                     ; sync                  ' 126   |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 146 (226)
            JP      psg_tone_write0     ;                       ' 10    |

com_psg_tone_w1                         ; 11                    ' 80
            wait108                     ; sync                  ' 108   |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 128 (208)
            JP      psg_tone_write1     ;                       ' 10    |

com_psg_tone_w2                         ; 12                    ' 80
            wait90                      ; sync                  ' 90    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 110 (190)
            JP      psg_tone_write2     ;                       ' 10    |

com_psg_tone_w3                         ; 13                    ' 80
            wait72                      ; sync                  ' 72    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 92 (172)
            JP      psg_tone_write3     ;                       ' 10    |

com_psg_tone_w4                         ; 14                    ' 80
            wait54                      ; sync                  ' 54    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 74 (154)
            JP      psg_tone_write4     ;                       ' 10    |

com_psg_tone_w5                         ; 15                    ' 80
            wait36                      ; sync                  ' 36    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 56 (136)
            JP      psg_tone_write5     ;                       ' 10    |

com_psg_tone_w6                         ; 16                    ' 80
            wait18                      ; sync                  ' 18    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 38 (118)
            JP      psg_tone_write6     ;                       ' 10    |

com_psg_tone_w7                         ; 17                    ' 80
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 20 (100)
            JP      psg_tone_write7     ;                       ' 10    |


psg_tone_write7                         ;                       ' 100
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (118)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write6
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (136)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write5
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (154)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write4
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (172)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write3
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (190)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write2
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (208)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write1
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (226)
            LD      (HL), A             ; write to PSG          ' 7     |

psg_tone_write0
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 18 (244)
            LD      (HL), A             ; write to PSG          ' 7     |

            JP      execute_xgm         ;                       ' 10    | (254)


com_psg_env_w0                          ; 18                    ' 80
            wait72                      ; sync                  ' 72    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 102 (182)
            LD      BC, PSG_ENV_SAV     ; BC point on PSG save  ' 10    |
            JP      psg_env_write0      ;                       ' 10    |

com_psg_env_w1                          ; 18                    ' 80
            wait10                      ; sync                  ' 10    |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 40 (120)
            LD      BC, PSG_ENV_SAV     ; BC point on PSG save  ' 10    |
            JP      psg_env_write1      ;                       ' 10    |

com_psg_env_w2                          ; 18                    ' 80
            wait154                     ; sync                  ' 154   |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 174 (254)
            LD      BC, PSG_ENV_SAV     ; BC point on PSG save  ' 10    |

            sampleOutput                ;                       ' 36    | (36)

            wait12                      ; sync                  ' 12    |
            JP      psg_env_write2      ;                       ' 10    | 22 (58)

com_psg_env_w3                          ; 18                    ' 80
            wait110                     ; sync                  ' 110   |
            LD      HL, PSGPORT         ; HL point on PSG port  ' 10    | 130 (210)
            LD      BC, PSG_ENV_SAV     ; BC point on PSG save  ' 10    |

psg_env_write3
            LD      A, (DE)             ; A = PSG data          ' 7     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            AND     $3                  ; A = channel number    ' 7     | 41 (254-3)
            ADD    (PSG_ENV_SAV & $FF)  ; add offset            ' 7     |
            LD      C, A                ; BC point on save      ' 4     |
            wait4                       ; sync                  ' 4     |

            sampleOutput                ;                       ' 36    | (36-3)

            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     | 25 (58)
            LD      (HL), A             ; write to PSG          ' 7     |
            LD      (BC), A             ; write to save         ' 7     |

psg_env_write2                          ;                       ' 58
            LD      A, (DE)             ; A = PSG data          ' 7     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            AND     $3                  ; A = channel number    ' 7     |
            ADD    (PSG_ENV_SAV & $FF)  ; add offset            ' 7     | 62 (120)
            LD      C, A                ; BC point on save      ' 4     |
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to PSG          ' 7     |
            LD      (BC), A             ; write to save         ' 7     |

psg_env_write1
            LD      A, (DE)             ; A = PSG data          ' 7     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            AND     $3                  ; A = channel number    ' 7     |
            ADD    (PSG_ENV_SAV & $FF)  ; add offset            ' 7     | 62 (182)
            LD      C, A                ; BC point on save      ' 4     |
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to PSG          ' 7     |
            LD      (BC), A             ; write to save         ' 7     |

psg_env_write0
            LD      A, (DE)             ; A = PSG data          ' 7     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            RLCA                        ;                       ' 4     |
            AND     $3                  ; A = channel number    ' 7     |
            ADD    (PSG_ENV_SAV & $FF)  ; add offset            ' 7     | 62 (244)
            LD      C, A                ; BC point on save      ' 4     |
            LD      A, (DE)             ; A = PSG data          ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to PSG          ' 7     |
            LD      (BC), A             ; write to save         ' 7     |

            JP      execute_xgm         ;                       ' 10    | (254)


; YM port0 command
; ----------------

com_ym_port0_w0                         ; 20                    ' 80
            wait36                      ; sync                  ' 36    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 66 (146)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_write0      ;                       ' 10    |

com_ym_port0_w1                         ; 21                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    | 30 (110)
            JP      ym_port_write1      ;                       ' 10    |

com_ym_port0_w2                         ; 22                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 82 (162)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_write2      ;                       ' 10    |

com_ym_port0_w3                         ; 23                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, HL              ; BC point on YM port0  ' 10    |
            INC     BC                  ; BC point on YM port1  ' 6     | 36 (116)
            JP      com_ym_port_w3      ; execute               ' 10    |

com_ym_port0_w4                         ; 24                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_write4      ;                       ' 10    |

com_ym_port0_w5                         ; 25                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 82 (162)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_write5      ;                       ' 10    |

com_ym_port0_w6                         ; 26                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, HL              ; BC point on YM port0  ' 10    |
            INC     BC                  ; BC point on YM port1  ' 6     | 36 (116)
            JP      com_ym_port_w6      ; execute               ' 10    |

com_ym_port0_w7                         ; 27                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_write7      ;                       ' 10    |

com_ym_port0_w8                         ; 28                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 82 (162)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_write8      ;                       ' 10    |

com_ym_port0_w9                         ; 29                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, HL              ; BC point on YM port0  ' 10    |
            INC     BC                  ; BC point on YM port1  ' 6     | 36 (116)
            JP      com_ym_port_w9      ; execute               ' 10    |

com_ym_port0_wA                         ; 2A                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_writeA      ;                       ' 10    |

com_ym_port0_wB                         ; 2B                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 82 (162)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_writeB      ;                       ' 10    |

com_ym_port0_wC                         ; 2C                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, HL              ; BC point on YM port0  ' 10    |
            INC     BC                  ; BC point on YM port1  ' 6     | 36 (116)
            JP      com_ym_port_wC      ; execute               ' 10    |

com_ym_port0_wD                         ; 2D                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 174 (254)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_writeD      ;                       ' 10    |

com_ym_port0_wE                         ; 2E                    ' 80
            wait36                      ; sync                  ' 36    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 66 (146)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            JP      ym_port_writeE      ;                       ' 10    |

com_ym_port0_wF                         ; 2F                    ' 80
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    | 30 (110)
            JP      ym_port_writeF      ;                       ' 10    |


; YM port2 command
; ----------------

com_ym_port2_w0                         ; 30                    ' 80
            wait36                      ; sync                  ' 36    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 66 (146)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_write0      ;                       ' 10    |

com_ym_port2_w1                         ; 31                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    | 30 (110)
            JP      ym_port_write1      ;                       ' 10    |

com_ym_port2_w2                         ; 32                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 82 (162)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_write2      ;                       ' 10    |

com_ym_port2_w3                         ; 33                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, HL              ; BC point on YM port2  ' 10    |
            INC     BC                  ; BC point on YM port3  ' 6     | 36 (116)
            JP      com_ym_port_w3      ; execute               ' 10    |

com_ym_port2_w4                         ; 34                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT2         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_write4      ;                       ' 10    |

com_ym_port2_w5                         ; 35                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 82 (162)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_write5      ;                       ' 10    |

com_ym_port2_w6                         ; 36                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, HL              ; BC point on YM port2  ' 10    |
            INC     BC                  ; BC point on YM port3  ' 6     | 36 (116)
            JP      com_ym_port_w6      ; execute               ' 10    |

com_ym_port2_w7                         ; 37                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT2         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_write7      ;                       ' 10    |

com_ym_port2_w8                         ; 38                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 82 (162)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_write8      ;                       ' 10    |

com_ym_port2_w9                         ; 39                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, HL              ; BC point on YM port2  ' 10    |
            INC     BC                  ; BC point on YM port3  ' 6     | 36 (116)
            JP      com_ym_port_w9      ; execute               ' 10    |

com_ym_port2_wA                         ; 3A                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT2         ; HL point on YM port0  ' 10    | 178 (254+4)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            EXX                         ;                       ' 4     |
            JP      ym_port_writeA      ;                       ' 10    |

com_ym_port2_wB                         ; 3B                    ' 80
            wait52                      ; sync                  ' 52    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 82 (162)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_writeB      ;                       ' 10    |

com_ym_port2_wC                         ; 3C                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, HL              ; BC point on YM port2  ' 10    |
            INC     BC                  ; BC point on YM port3  ' 6     | 36 (116)
            JP      com_ym_port_wC      ; execute               ' 10    |

com_ym_port2_wD                         ; 3D                    ' 80
            wait144                     ; sync                  ' 144   |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 174 (254)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_writeD      ;                       ' 10    |

com_ym_port2_wE                         ; 3E                    ' 80
            wait36                      ; sync                  ' 36    |
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    | 66 (146)
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    |
            JP      ym_port_writeE      ;                       ' 10    |

com_ym_port2_wF                         ; 3F                    ' 80
            LD      HL, YMPORT2         ; HL point on YM port2  ' 10    |
            LD      BC, YMPORT3         ; BC point on YM port3  ' 10    | 30 (110)
            JP      ym_port_writeF      ;                       ' 10    |


com_ym_port_w3                          ;                       ' 116
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (152)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |
            JP      ym_port_write2      ;                       ' 10    | (162)

com_ym_port_w6                          ;                       ' 116
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (152)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |
            JP      ym_port_write5      ;                       ' 10    | (162)

com_ym_port_w9                          ;                       ' 116
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (152)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |
            JP      ym_port_write8      ;                       ' 10    | (162)

com_ym_port_wC                          ;                       ' 116
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (152)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |
            JP      ym_port_writeB      ;                       ' 10    | (162)


ym_port_write1                          ;                       ' 110
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (146)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |

ym_port_write0
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (212)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            EXX                         ; switch to HL' (port0) ' 4     |
.wait                                   ;                       '       |
            BIT     7, (HL)             ; test YM ready bit     ' 12    |
            JP      NZ, .wait           ; wait while busy       ' 10    | 40 (252)
                                        ;                       '       |
            LD      (HL), $2A           ; restore DAC write     ' 10    |
            EXX                         ; switch back           ' 4     |

            JP      execute_xgm         ; +8 cycles here        ' 10    | (254+8)

ym_port_writeF                          ;                       ' 110
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            LD      (HL), A             ; write reg num to YM   ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     | 36 (146)
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write data to YM      ' 7     |

ym_port_writeE
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (212)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            wait12                      ; sync                  ' 12    |
            waitYMReady                 ; wait YM to be ready   ' 30    | 42 (254)

ym_port_writeD
            sampleOutputSafe            ;                       ' 46    | (46)

            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (112)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

;ym_port_writeC
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (178)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

ym_port_writeB
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (244)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            EXX                         ; switch to HL' (port0) ' 4     | (248)
.wait                                   ;                       '       |
            BIT     7, (HL)             ; test YM ready bit     ' 12    |
            JP      NZ, .wait           ; wait while busy       ' 10    | 22 (258+14)

ym_port_writeA                          ;                       ' 4
            LD      (HL), $2A           ; restore DAC write     ' 10    |
            LD      A, (BC)             ;                       ' 7     |
            INC     BC                  ; play sample           ' 6     | 42 (46)
            RES     2, B                ;                       ' 8     |
            LD      (DE), A             ;                       ' 7     |
            EXX                         ;                       ' 4     |

            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (112)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

;ym_port_write9
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (178)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

ym_port_write8
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (244)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            EXX                         ; switch to HL' (port0) ' 4     | (248)
.wait                                   ;                       '       |
            BIT     7, (HL)             ; test YM ready bit     ' 12    |
            JP      NZ, .wait           ; wait while busy       ' 10    | 22 (258+14)

ym_port_write7                          ;                       ' 4
            LD      (HL), $2A           ; restore DAC write     ' 10    |
            LD      A, (BC)             ;                       ' 7     |
            INC     BC                  ; play sample           ' 6     | 42 (46)
            RES     2, B                ;                       ' 8     |
            LD      (DE), A             ;                       ' 7     |
            EXX                         ;                       ' 4     |

            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (112)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

;ym_port_write6
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (178)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

ym_port_write5
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (244)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            EXX                         ; switch to HL' (port0) ' 4     | (248)
.wait                                   ;                       '       |
            BIT     7, (HL)             ; test YM ready bit     ' 12    |
            JP      NZ, .wait           ; wait while busy       ' 10    | 22 (258+14)

ym_port_write4                          ;                       ' 4
            LD      (HL), $2A           ; restore DAC write     ' 10    |
            LD      A, (BC)             ;                       ' 7     |
            INC     BC                  ; play sample           ' 6     | 42 (46)
            RES     2, B                ;                       ' 8     |
            LD      (DE), A             ;                       ' 7     |
            EXX                         ;                       ' 4     |

            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (112)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

;ym_port_write3
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (178)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

ym_port_write2
            LD      A, (DE)             ; A = YM reg num        ' 7     |
            waitYMReady                 ; wait YM to be ready   ' 30    |
            LD      (HL), A             ; write reg num to YM   ' 7     | 66 (244)
            INC     E                   ; next data             ' 4     |
            LD      A, (DE)             ; A = YM reg data       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (BC), A             ; write to YM           ' 7     |

            EXX                         ; switch to HL' (port0) ' 4     | (248)
.wait                                   ;                       '       |
            BIT     7, (HL)             ; test YM ready bit     ' 12    |
            JP      NZ, .wait           ; wait while busy       ' 10    | 22 (258+14)

            LD      (HL), $2A           ; restore DAC write     ' 10    |
            LD      A, (BC)             ;                       ' 7     |
            INC     BC                  ; play sample           ' 6     | 42 (46)
            RES     2, B                ;                       ' 8     |
            LD      (DE), A             ;                       ' 7     |
            EXX                         ;                       ' 4     |

            wait24                      ; sync                  ' 24    | 54 (100)
            waitYMReady                 ; wait YM to be ready   ' 30    |

            JP      ym_port_write1      ; execute next          ' 10    | (110)


; YM KEY command
; --------------

com_ym_key_w0                           ; 40                    ' 80
            wait44                      ; sync                  ' 44    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    | 84 (164)
            LD      (HL), $28           ; set reg num to YM     ' 10    |
            JP      ym_key_write0       ;                       ' 10    |

com_ym_key_w1                           ; 41                    ' 80
            wait4                       ; sync                  ' 4     |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    | 44 (124)
            LD      (HL), $28           ; set reg num to YM     ' 10    |
            JP      ym_key_write1       ;                       ' 10    |

com_ym_key_w2                           ; 43                    ' 80
            wait134                     ; sync                  ' 134   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            LD      (HL), $28           ; set reg num to YM     ' 10    | 174 (254)
            JP      ym_key_write2       ;                       ' 10    |

com_ym_key_w3                           ; 44                    ' 80
            wait64                      ; sync                  ' 64    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            LD      (HL), $28           ; set reg num to YM     ' 10    | 104 (184)
            JP      ym_key_write3       ;                       ' 10    |

com_ym_key_w4                           ; 45                    ' 80
            wait24                      ; sync                  ' 24    |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |
            LD      (HL), $28           ; set reg num to YM     ' 10    | 64 (144)
            JP      ym_key_write4       ;                       ' 10    |

com_ym_key_w5                           ; 46                    ' 80
            wait154                     ; sync                  ' 174   |
            LD      HL, YMPORT0         ; HL point on YM port0  ' 10    | 174 (254)
            LD      BC, YMPORT1         ; BC point on YM port1  ' 10    |

            sampleOutputSafe            ;                       ' 46    | (46)

            wait16                      ; sync                  ' 16    |
            waitYMReadyFast             ; wait YM to be ready   ' 22    |
            LD      (HL), $28           ; set reg num to YM     ' 10    | 58 (104)
            JP      ym_key_write5       ;                       ' 10    |


ym_key_write1                           ;                       ' 124
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (164)
            LD      (BC), A             ; write to YM           ' 7     |

ym_key_write0                           ;                       ' 164
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (204)
            LD      (BC), A             ; write to YM           ' 7     |

            wait8                       ; sync                  ' 8     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (244)
            LD      (HL), $2A           ; restore DAC write     ' 10    |

            JP      execute_xgm         ;                       ' 10    | (254)

ym_key_write5                           ;                       ' 104
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (144)
            LD      (BC), A             ; write to YM           ' 7     |

ym_key_write4                           ;                       ' 144
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (184)
            LD      (BC), A             ; write to YM           ' 7     |

ym_key_write3                           ;                       ' 184
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (224)
            LD      (BC), A             ; write to YM           ' 7     |

            wait8                       ; sync                  ' 8     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 30 (254)

ym_key_write2
            sampleOutputSafe            ;                       ' 46    | (46)

            wait8                       ; sync                  ' 8     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (86)
            LD      (HL), $28           ; restore write to key  ' 10    |

            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     DE                  ; next data             ' 6     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (126)
            LD      (BC), A             ; write to YM           ' 7     |

.write1
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (166)
            LD      (BC), A             ; write to YM           ' 7     |

.write0
            LD      A, (DE)             ; A = YM key data       ' 7     |
            INC     E                   ; next data             ' 4     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 40 (206)
            LD      (BC), A             ; write to YM           ' 7     |

            wait7                       ; sync                  ' 7     |
            waitYMReadyFast             ; wait YM to be ready   ' 22    | 39 (245)
            LD      (HL), $2A           ; restore DAC write     ' 10    |

            JP      execute_xgm         ;                       ' 10    | (254+1)


; PCM command
; -----------

com_pcm_p0_ch0                          ; 50                    ' 80
            LD      C, 0                ; C = prio              ' 7     |
            JP      com_pcm_ch0         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p1_ch0                          ; 54                    ' 80
            LD      C, 1                ; C = prio              ' 7     |
            JP      com_pcm_ch0         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p2_ch0                          ; 58                    ' 80
            LD      C, 2                ; C = prio              ' 7     |
            JP      com_pcm_ch0         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p3_ch0                          ; 5C                    ' 80
            LD      C, 3                ; C = prio              ' 7     |
            JP      com_pcm_ch0         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p0_ch1                          ; 51                    ' 80
            LD      C, 0                ; C = prio              ' 7     |
            JP      com_pcm_ch1         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p1_ch1                          ; 55                    ' 80
            LD      C, 1                ; C = prio              ' 7     |
            JP      com_pcm_ch1         ; execute PCM com       ' 10    | 17 (97)

com_pcm_p2_ch1                          ; 59                    ' 80
            LD      C, 2                ; C = prio              ' 7     |
            JP      com_pcm_ch1         ; execute PCM com       ' 10    | (119)

com_pcm_p3_ch1                          ; 5D                    ' 80
            LD      C, 3                ; C = prio              ' 7     |
            JP      com_pcm_ch1         ; execute PCM com       ' 10    | (119)

com_pcm_p0_ch2                          ; 52                    ' 80
            LD      C, 0                ; C = prio              ' 7     |
            JP      com_pcm_ch2         ; execute PCM com       ' 10    | (119)

com_pcm_p1_ch2                          ; 56                    ' 80
            LD      C, 1                ; C = prio              ' 7     |
            JP      com_pcm_ch2         ; execute PCM com       ' 10    | (119)

com_pcm_p2_ch2                          ; 5A                    ' 80
            LD      C, 2                ; C = prio              ' 7     |
            JP      com_pcm_ch2         ; execute PCM com       ' 10    | (119)

com_pcm_p3_ch2                          ; 5E                    ' 80
            LD      C, 3                ; C = prio              ' 7     |
            JP      com_pcm_ch2         ; execute PCM com       ' 10    | (119)

com_pcm_p0_ch3                          ; 53                    ' 80
            LD      C, 0                ; C = prio              ' 7     |
            JP      com_pcm_ch3         ; execute PCM com       ' 10    | (119)

com_pcm_p1_ch3                          ; 57                    ' 80
            LD      C, 1                ; C = prio              ' 7     |
            JP      com_pcm_ch3         ; execute PCM com       ' 10    | (119)

com_pcm_p2_ch3                          ; 5B                    ' 80
            LD      C, 2                ; C = prio              ' 7     |
            JP      com_pcm_ch3         ; execute PCM com       ' 10    | (119)

com_pcm_p3_ch3                          ; 5F                    ' 80
            LD      C, 3                ; C = prio              ' 7     |
            JP      com_pcm_ch3         ; execute PCM com       ' 10    | (119)


com_pcm_ch0                             ;                       ' 97
            handlePCMCommandXGM 0       ; handle command        ' 142   |
            wait4                       ; sync                  ' 4     | 156 (254-1)
            JP      execute_xgm         ;                       ' 10    |

com_pcm_ch1                             ;                       ' 97
            handlePCMCommandXGM 1       ; handle command        ' 142   |
            wait4                       ; sync                  ' 4     | 156 (254-1)
            JP      execute_xgm         ;                       ' 10    |

com_pcm_ch2                             ;                       ' 97
            handlePCMCommandXGM 2       ; handle command        ' 142   |
            wait4                       ; sync                  ' 4     | 156 (254-1)
            JP      execute_xgm         ;                       ' 10    |

com_pcm_ch3                             ;                       ' 97
            handlePCMCommandXGM 3       ; handle command        ' 142   |
            wait4                       ; sync                  ' 4     | 156 (254-1)
            JP      execute_xgm         ;                       ' 10    |



; STATE command
; -------------

com_state_w0                            ; 60                    ' 80
            wait114                     ; sync                  ' 114   |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 131 (211)
            JP      state_w0            ; execute               ' 10    |

com_state_w1                            ; 61                    ' 80
            wait81                      ; sync                  ' 81    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 98 (178)
            JP      state_w1            ; execute               ' 10    |

com_state_w2                            ; 62                    ' 80
            wait48                      ; sync                  ' 48    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 65 (145)
            JP      state_w2            ; execute               ' 10    |

com_state_w3                            ; 63                    ' 80
            wait15                      ; sync                  ' 15    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 32 (112)
            JP      state_w3            ; execute               ' 10    |

com_state_w4                            ; 64                    ' 80
            wait174                     ; sync                  ' 174   | (254)
            sampleOutput                ; sample output         ' 36    | (36)

            wait26                      ; sync                  ' 26    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 43 (79)
            JP      state_w4            ; execute               ' 10    |

com_state_w5                            ; 65                    ' 80
            wait157                     ; sync                  ' 157   |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 174 (254)
            JP      state_w5            ; execute               ' 10    |

com_state_w6                            ; 66                    ' 80
            wait124                     ; sync                  ' 124   |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 141 (221)
            JP      state_w6            ; execute               ' 10    |

com_state_w7                            ; 67                    ' 80
            wait91                      ; sync                  ' 91    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 108 (188)
            JP      state_w7            ; execute               ' 10    |

com_state_w8                            ; 68                    ' 80
            wait58                      ; sync                  ' 58    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 75 (155)
            JP      state_w8            ; execute               ' 10    |

com_state_w9                            ; 69                    ' 80
            wait25                      ; sync                  ' 25    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 42 (122)
            JP      state_w9            ; execute               ' 10    |

com_state_wA                            ; 6A                    ' 80
            wait174                     ; sync                  ' 174   | (254)
            sampleOutput                ; sample output         ' 36    | (36)

            wait36                      ; sync                  ' 36    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 53 (89)
            JP      state_wA            ; execute               ' 10    |

com_state_wB                            ; 6B                    ' 80
            wait157                     ; sync                  ' 157   |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 174 (254)
            JP      state_wB            ; execute               ' 10    |

com_state_wC                            ; 6C                    ' 80
            wait124                     ; sync                  ' 124   |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 141 (221)
            JP      state_wC            ; execute               ' 10    |

com_state_wD                            ; 6D                    ' 80
            wait91                      ; sync                  ' 91    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 108 (188)
            JP      state_wD            ; execute               ' 10    |

com_state_wE                            ; 6E                    ' 80
            wait58                      ; sync                  ' 58    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 75 (155)
            JP      state_wE            ; execute               ' 10    |

com_state_wF                            ; 6F                    ' 80
            wait35                      ; sync                  ' 35    |
            LD      H, (YM_RR_SAV >> 8) ; H point on YM save H  ' 7     | 42 (122)

state_wF                                ;                       ' 122
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (155)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_wE
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (188)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_wD
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (221)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_wC
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (254)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_wB
            sampleOutput                ; sample output         ' 36    |
            wait20                      ; wait                  ' 20    | (56)

            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (89)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_wA
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (122)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w9
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (155)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w8
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (188)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w7
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (221)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w6
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (254)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w5
            sampleOutput                ; sample output         ' 36    |
            wait10                      ; wait                  ' 10    | (46)

            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (79)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w4
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (112)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w3
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (145)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w2
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (178)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w1
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (211)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

state_w0
            LD      A, (DE)             ; A = state register    ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      L, A                ; HL point on save      ' 4     | 33 (244)
            LD      A, (DE)             ; A = state value       ' 7     |
            INC     E                   ; next data             ' 4     |
            LD      (HL), A             ; write to save         ' 7     |

            JP      execute_xgm         ;                       ' 10    | (254)


; NULL command
; ------------

com_null                                ; 01-0F / 60-7C         ' 80
            wait164                     ; sync                  ' 164   |
            JP      execute_xgm         ;                       ' 10    | 174 (254)


; EXTRA FRAME command
; -------------------

com_extra_frm                           ; 7D                            ' 80

            LD      A, $1               ;                               ' 7     |
            LD      (MODIFYING_F), A    ; we are modifying variable     ' 13    | 20 (100)

            LD      A, (PENDING_FRM)    ;                               ' 13    |
            INC     A                   ; force process 1 more frame    ' 4     | 30 (130)
            LD      (PENDING_FRM), A    ;                               ' 13    |

            XOR     A                   ;                               ' 4     |
            LD      (MODIFYING_F), A    ; no more modifying variable    ' 13    | 17 (147)

            LD      HL, ELAPSED         ;                               ' 10    | 10 (157)

            LD      A, (HL)             ;                               ' 7     |
            SUB     A, #1               ;                               ' 7     |
            LD      (HL), A             ;                               ' 7     |
            INC     L                   ;                               ' 4     |
            LD      A, (HL)             ;                               ' 7     |
            SBC     A, #0               ; fix elapsed frame (24 bit)    ' 7     | 71 (228)
            LD      (HL), A             ;                               ' 7     |
            INC     L                   ;                               ' 4     |
            LD      A, (HL)             ;                               ' 7     |
            SBC     A, #0               ;                               ' 7     |
            LD      (HL), A             ;                               ' 7     |

            wait16                      ; sync                          ' 16    | 26 (254)
            JP      execute_xgm         ;                               ' 10    |


; LOOP command
; ------------

com_loop                                ; 7E                        ' 80

            LD      A, (REM_LOOP)       ; A = remaining loop        ' 13    |
            DEC     A                   ; 0 mean infinite loop      ' 4     |
            LD      (REM_LOOP), A       ; A = remaining loop        ' 13    | 40 (120)
            JP      NZ, .continue       ;                           ' 10    |

            LD      HL, COMMAND         ; HL = COMMAND              ' 10    | 25 (145)
            SET     XGM_PAUSE_SFT, (HL) ; request pause/end XGM     ' 15    |

            LD      HL, STATUS          ; HL = STATUS               ' 10    | 25 (170)
            RES     XGM_PLAY_SFT, (HL)  ; clear play status         ' 15    |

            INC     E                   ; next param                ' 4     |
            INC     E                   ;                           ' 4     | 12 (182)
            INC     E                   ;                           ' 4     |

            wait62                      ; sync                      ' 62    |
            JP      execute_xgm         ;                           ' 10    | 72 (254)

.continue
            LD      A, (DE)             ; A = loop addr b7-b0       ' 7     |
            LD      L, A                ; L = loop addr b7-b0       ' 4     |
            INC     E                   ; next param                ' 4     |
            LD      A, (DE)             ; A = loop addr b15-b8      ' 7     | 30 (150)
            LD      H, A                ; HL = loop addr b15-b0     ' 4     |
            INC     E                   ; next param                ' 4     |

            LD      BC, (XGM_ARG_A+0)   ; BC = XGM base addr (ML)   ' 20    |
            ADD     HL, BC              ; HL = XGM base addr + loop ' 11    | 47 (197)
            LD      (XGM_ADDR+0), HL    ; set new XGM addr (ML)     ' 16    |

            LD      A, (DE)             ; A = loop addr b23-b16     ' 7     |
            LD      HL, XGM_ARG_A+2     ; HL point XGM base adr H   ' 10    |
            ADC     (HL)                ; A = XGM base addr + loop  ' 7     | 41 (238)
            INC     E                   ; next param                ' 4     |
            LD      (XGM_ADDR+2), A     ; set new XGM addr (H)      ' 13    |

            XOR     A                   ; force end frame           ' 4     |
            LD      (DE), A             ; after loop taken          ' 7     | 11 (249)

            JP      execute_xgm         ; +5 cycles delay here..    ' 10    | (254+5)


; END XGM command
; ---------------

com_end                                 ; 7F                        ' 80
            LD      HL, COMMAND         ; HL = COMMAND              ' 10    | 25 (105)
            SET     XGM_PAUSE_SFT, (HL) ; request pause/end XGM     ' 15    |

            LD      HL, STATUS          ; HL = STATUS               ' 10    | 25 (130)
            RES     XGM_PLAY_SFT, (HL)  ; clear play status         ' 15    |

            wait114                     ; sync                      ' 114   |
            JP      execute_xgm         ;                           ' 10    | 124 (254)


; $BC+X+Y+Z
do_xgm_again                            ;                           ' 135
            DEC     L                   ; HL point on MODIFYING_F   ' 4     |
            LD      (HL), $0            ; no more modifying         ' 10    | 14 (149)

            wait95                      ; sync                      ' 95    |
            JP      main_loop           ; restart loop (prep xgm)   ' 10    | 105 (254)



xgm_stopped
            LD      (HL), $0            ; clear PENDING_FRM         ' xx
            JP      xgm_stopped_cont    ; continue                  ' xx


; $BC+X+Y+Z
xgm_done                                ;                               ' 90

;    LD  A, (VCOUNTER)
;    LD  (DEBUG_8), A

            LD      HL, MODIFYING_F     ;                               ' 10    |
            LD      (HL), $1            ; we are modifying variable     ' 10    | 24 (114)
            INC     L                   ; HL point on PENDING_FRM       ' 4     |

            DEC     (HL)                ; still have frame to process ? ' 11    |
            JP      M, xgm_stopped      ; for safety on 68k clear       ' xx
            JP      NZ, do_xgm_again    ; do XGM process again          ' 10    | 21 (135)

xgm_stopped_cont
            DEC     L                   ; HL point on MODIFYING_F       ' 4     |
            LD      (HL), $0            ; no more modifying variable    ' 10    | 14 (149)


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_9), A

; execute external command
; ------------------------

; $BC+X+Y+Z
external_com                            ;                           ' 149
            LD      SP, STACK           ; restore stack             ' 10    |
            LD      HL, COMMAND         ; HL point on COMMAND       ' 10    | 20 (159)

.chk_xgm_play
            BIT     XGM_PLAY_SFT, (HL)  ; play XGM command ?        ' 12    |
            JP      Z, .chk_xgm_resume  ;                           ' 10    | 22 (181)

            RES     XGM_PLAY_SFT, (HL)  ; clear command             ' 15    |
            INC     L                   ;                           ' 4     |
            INC     L                   ; HL point on status        ' 4     | 38 (219)
            SET     XGM_PLAY_SFT, (HL)  ; set play status           ' 15    |

            LD      HL, (XGM_ARG_A)     ; set new XGM address       ' 16    |
            LD      (XGM_ADDR), HL      ;                           ' 16    | 58 (277)
            LD      A, (XGM_ARG_A+2)    ;                           ' 13    |
            LD      (XGM_ADDR+2),A      ;                           ' 13    |

            LD      A, (LOOP_ARG)       ; set remaining loop        ' 13    |
            LD      (REM_LOOP),A        ;                           ' 13    | 26 (303)

            LD      HL, #0000           ; clear elapsed frame       ' 10    |
            LD      (ELAPSED), HL       ; (24 bit counter)          ' 16    |
            XOR     A                   ;                           ' 4     | 43 (346)
            LD      (ELAPSED+2), A      ;                           ' 13    |

            LD      HL, COMMAND         ; restore HL                ' 10    |
            JP      external_com_pcm    ; do PCM commands           ' 10    | 20 (254+112)
                                        ; +112 cycles here, ignore

.chk_xgm_resume
            BIT     XGM_RESUME_SFT, (HL)    ; play XGM command ?    ' 12    |
            JP      Z, .chk_xgm_stop        ;                       ' 10    | 22 (203)

            RES     XGM_RESUME_SFT, (HL)    ; clear command         ' 15    |
            INC     L                       ;                       ' 4     |
            INC     L                       ; HL point on status    ' 4     | 38 (241)
            SET     XGM_PLAY_SFT, (HL)      ; set play status       ' 15    |

            LD      DE, YM_RR_SAV       ; DE point on save state    ' 10    |
            LD      SP, STACK           ; set STACK                 ' 10    | 20 (254+7)
                                        ; +7 cycles here, ignore

            CALL    loadState           ; load state                ' 140+  | (140)

            wait94                      ; sync                      ' 94    |
            LD      HL, COMMAND         ; restore HL                ' 10    | 114 (254)
            JP      external_com_pcm    ; do PCM commands           ' 10    |

.chk_xgm_stop
            BIT     XGM_PAUSE_SFT, (HL) ; pause/end XGM command ?   ' 12    |
            JP      Z, .chk_pcm0_pre    ;                           ' 10    | 22 (225)

            RES     XGM_PAUSE_SFT, (HL) ; clear command             ' 15    |
            INC     L                   ;                           ' 4     |
            INC     L                   ; HL point on status        ' 4     | 38 (263)
            RES     XGM_PLAY_SFT, (HL)  ; clear play status         ' 15    |

            LD      DE, YM_RR_OFF       ; DE point on off state     ' 10    |
            LD      SP, STACK           ; set STACK                 ' 10    | 20 (254+29)
                                        ; +29 cycles here, ignore

            CALL    loadState           ; stop music                ' 140+  | (140)

            LD      HL, YMPORT0         ; HL point on YM port0      ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1      ' 10    | 20 (160)

            LD      (HL), $28           ; set reg num to YM         ' 10    |
            waitYMReadyFast             ; wait YM to be ready       ' 22    | 32 (192)

            XOR     A                   ; A = $00                   ' 4     |
            LD      (BC), A             ; key off - all channel     ' 7     |
            INC     A                   ; A = $01                   ' 4     | 37 (229)
            waitYMReadyFast             ; wait YM to be ready       ' 22    |

            LD      (BC), A             ; key off - all channel     ' 7     |
            waitYMReadyFast             ; wait YM to be ready       ' 22    | 29 (254+4)

            sampleOutputSafe            ; *** sample output ****    ' 46    | (46+4)

            waitYMReadyFast             ; wait YM to be ready       ' 22    |
            LD      (HL), $28           ; set reg num to YM         ' 10    |
            LD      A, $02              ; A = $02                   ' 7     | 61 (111)
            waitYMReadyFast             ; wait YM to be ready       ' 22    |

            LD      (BC), A             ; key off - all channel     ' 7     |
            ADD     $2                  ; A = $04                   ' 7     | 36 (147)
            waitYMReadyFast             ; wait YM to be ready       ' 22    |

            LD      (BC), A             ; key off - all channel     ' 7     |
            INC     A                   ; A = $05                   ' 4     | 33 (180)
            waitYMReadyFast             ; wait YM to be ready       ' 22    |

            LD      (BC), A             ; key off - all channel     ' 7     |
            INC     A                   ; A = $06                   ' 4     | 33 (213)
            waitYMReadyFast             ; wait YM to be ready       ' 22    |

            LD      (BC), A             ; key off - all channel     ' 7     |
            waitYMReadyFast             ; wait YM to be ready       ' 22    | 39 (252)
            LD      (HL), $2A           ; restore DAC write         ' 10    |

            LD      HL, COMMAND         ; restore HL                ' 10    |
            JP      external_com_pcm    ; continue                  ' 10    | 20 (254+18)
                                        ; +18 cycles here, ignore

.chk_pcm0_pre                           ;                           ' 225
            LD      HL, COMMAND         ; restore HL                ' 10    |
            wait19                      ; sync                      ' 19    | 29 (254)

; $BD+X+Y+Z
external_com_pcm
            sampleOutput                ; *** sample output ****    ' 36    | (36)

.chk_pcm0
            BIT     CH0_SFT, (HL)       ; play PCM0 command ?       ' 12    |
            JP      Z, .chk_pcm1        ;                           ' 10    | 22 (58)

            RES     CH0_SFT, (HL)       ; clear command             ' 15    | (73)

            handlePCMCommand 0          ; handle play PCM command   ' 157   | 167 (240)
            LD      HL, COMMAND         ; restore HL                ' 10    |

            wait14                      ; sync                      ' 14    | (254)

            sampleOutput                ; *** sample output ****    ' 36    | (36)

            wait22                      ; sync                      ' 22    | (58)

.chk_pcm1
            BIT     CH1_SFT, (HL)       ; play PCM1 command ?       ' 12    |
            JP      Z, .chk_pcm2        ;                           ' 10    | 22 (80)

            RES     CH1_SFT, (HL)       ; clear command             ' 15    | (95)

            handlePCMCommand 1          ; handle play PCM command   ' 157   | (254-2)

            sampleOutput                ; *** sample output ****    ' 36    | (36-2)

            wait36                      ; sync                      ' 36    | 46 (80)
            LD      HL, COMMAND         ; restore HL                ' 10    |

.chk_pcm2
            BIT     CH2_SFT, (HL)       ; play PCM2 command ?       ' 12    |
            JP      Z, .chk_pcm3        ;                           ' 10    | 22 (102)

            RES     CH2_SFT, (HL)       ; clear command             ' 15    | (117)

            handlePCMCommand 2          ; handle play PCM command   ' 157   | (254+20)

            sampleOutput                ; *** sample output ****    ' 36    | (36+20)

            wait36                      ; sync                      ' 36    | 46 (102)
            LD      HL, COMMAND         ; restore HL                ' 10    |

.chk_pcm3
            BIT     CH3_SFT, (HL)       ; play PCM3 command ?       ' 12    |
            JP      Z, ext_com_done     ;                           ' 10    | 22 (124)

            RES     CH3_SFT, (HL)       ; clear command             ' 15    | (139)

            handlePCMCommand 3          ; handle play PCM command   ' 157   | (254+42)

            sampleOutput                ; *** sample output ****    ' 36    | (36+42)

            wait46                      ; sync                      ' 46    | (124)

; $BD+X+Y+Z
ext_com_done                                ;                           ' 124
            LD      BC, STATUS              ; BC point on STATUS        ' 10    |
            LD      A, (BC)                 ; A = STATUS                ' 7     | 24 (148)
            AND     $F0                     ; clear PCM play status     ' 7     |

            LD      HL, (ID_TABLE+0)        ; HL = null PCM addr        ' 16    |
            LD      DE, (PCM_ADDR+(0*8))    ; DE = PCM ch0 addr         ' 20    |
            SBC     HL, DE                  ; HL == DE ?                ' 15    | 63 (211)
            JR      Z, .ch0_silent          ; ch0 is not playing        ' 12    |

            OR      A, $01                  ; set play status for ch0   ' +2

.ch0_silent
            EX      AF, AF'                 ; preserve AF               ' 4     |
            wait35                          ; sync                      ' 35    | 39 (254-4)

; $BE+X+Y+Z
            sampleOutput                    ; sample output             ' 36-4  | (36)
            EX      AF, AF'                 ; restore AF                ' 4     |

            LD      HL, (ID_TABLE+0)        ; HL = null PCM addr        ' 16    |
            LD      DE, (PCM_ADDR+(1*8))    ; DE = PCM ch1 addr         ' 20    |
            SBC     HL, DE                  ; HL == DE ?                ' 15    | 63 (99)
            JR      Z, .ch1_silent          ; ch1 is not playing        ' 12    |

            OR      A, $02                  ; set play status for ch1   ' +2

.ch1_silent
            LD      HL, (ID_TABLE+0)        ; HL = null PCM addr        ' 16    |
            LD      DE, (PCM_ADDR+(2*8))    ; DE = PCM ch2 addr         ' 20    |
            SBC     HL, DE                  ; HL == DE ?                ' 15    | 63 (162)
            JR      Z, .ch2_silent          ; ch2 is not playing        ' 12    |

            OR      A, $04                  ; set play status for ch2   ' +2

.ch2_silent
            LD      HL, (ID_TABLE+0)        ; HL = null PCM addr        ' 16    |
            LD      DE, (PCM_ADDR+(3*8))    ; DE = PCM ch3 addr         ' 20    |
            SBC     HL, DE                  ; HL == DE ?                ' 15    | 63 (225)
            JR      Z, .ch3_silent          ; ch3 is not playing        ' 12    |

            OR      A, $08                  ; set play status for ch3   ' +2

.ch3_silent
            LD      (BC), A                 ; set new status            ' 7     | (229)

            LD      A, (PROTECT_ARG)        ; get BUS protect state     ' 13    |
            OR      A                       ; no protecting ?           ' 4     | 27 (254+2)
            JP      Z, main_loop            ; process next frame        ' 10    |
                                            ; +2 cycles here, ignore...

; frame done
;
; 1 frame of PCM sample (256 bytes) is ~287 lines.
;
; NTSC: we should be <= 0xE8 to respect frame time here and we need to update PCM buffer 0.9 time per frame
; so we have a free PCM mix frame from time to time.
; Median (X+Y+Z) should be < 0x2A (< 42) plus some lines from the free PCM mix
;
; PAL: we should be <= 0118 to respect frame time here but we need to update PCM buffer 1.1 time per frame
; so we have a penalty PCM mix frame from time to time.
; Median (X+Y+Z) should be < 0x5A (< 90) minus some lines for the extra PCM mix
; -------------------------------------------------------------------------------------


;    LD  A, (VCOUNTER)
;    LD  (DEBUG_A), A

; BUS protection wait (to avoid BUS contention with DMA)
;
; $BF+X+Y+Z

bus_protect_wait
            sampleOutput                ; *** sample output *** ' 36    | (36)

            wait105                     ; sync                  ' 105   | (141)

            LD      A, (PROTECT_ARG)    ; get BUS protect state ' 13    |
            OR      A                   ; no more protecting ?  ' 4     | 27 (168)
            JP      Z, .continue        ; continue              ' 10    |

            LD      HL, (IDLE_LOOP)     ;                       ' 16    |
            INC     HL                  ; increment idle loop   ' 6     | 38 (206)
            LD      (IDLE_LOOP), HL     ;                       ' 16    |

            LD      HL, (WAIT_LOOP)     ;                       ' 16    |
            INC     HL                  ; increment wait loop   ' 6     | 38 (244)
            LD      (WAIT_LOOP), HL     ;                       ' 16    |

            JP      bus_protect_wait    ; wait until bus is ok  ' 10    | (254)

.continue                               ;                       ' 168

;    LD  A, (VCOUNTER)
;    LD  (DEBUG_B), A

            wait76                      ; sync                  ' 76    |
            JP      main_loop           ; process next frame    ' 10    | 86 (254)



; ##############################  functions  ################################

            INCLUDE "z80_fct.i80"   ; basic functions


; loadState
; ---------
;     ?       ->  HL  -> ?
;     ?       ->  BC  -> ?
; reg source  ->  DE  -> ?
;
; load the YM and PSG state
; = 8 samples + 140 cycles

loadState

            sampleOutput                ; *** sample output ****    ' 36    | (36)

            LD      HL, YMPORT0         ; HL point on YM port0      ' 10    |
            LD      BC, YMPORT1         ; BC point on YM port1      ' 10    | 20 (56)

            CALL    loadYMState         ; load YM state             ' 188+  | (244)

            LD      HL, YMPORT2         ; HL point on YM port2      ' 10    |
            LD      BC, YMPORT3         ; BC point on YM port3      ' 10    | 20 (254+10)

            sampleOutputSafe            ; *** sample output ****    ' 46    | (46+10)

            CALL    loadYMState         ; load YM state             ' 188+  | (244)

            LD      HL, PSGPORT         ; HL point on PSG           ' 10    | (254)

            sampleOutputSafe            ; *** sample output ****    ' 46    | (46+10)

            JP      loadPSGState        ; load PSG state            ' 10+94 | (140)


; loadYMState
; -----------
; YMPORT0/2   ->  HL
; YMPORT1/3   ->  BC
; reg source  ->  DE  -> ?
;
; load the YM RR state
; = 4 samples (244 cycles)

loadYMState                             ;                           ' 56

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $80           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 62 (118)
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $81           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (180)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $82           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (242)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            EXX                         ;                           ' 4     | (246)

.wait1
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait1          ; wait while busy           ' 10    | 22 (254+14)

            LD      (HL), $2A           ;                           ' 10    |
            LD      A, (BC)             ;                           ' 7     |
            INC     BC                  ; *** sample output ****    ' 6     | 38 (52)
            RES     2, B                ;                           ' 8     |
            LD      (DE), A             ;                           ' 7     |

.wait2
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait2          ; wait while busy           ' 10    | 22 (74)

            EXX                         ;                           ' 4     | (78)

            LD      (HL), $84           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 32 (110)
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $85           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (172)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $86           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (234)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            EXX                         ;                           ' 4     | (238)

.wait3
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait3          ; wait while busy           ' 10    | 22 (254+6)

            LD      (HL), $2A           ;                           ' 10    |
            LD      A, (BC)             ;                           ' 7     |
            INC     BC                  ; *** sample output ****    ' 6     | 38 (44)
            RES     2, B                ;                           ' 8     |
            LD      (DE), A             ;                           ' 7     |

.wait4
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait4          ; wait while busy           ' 10    | 22 (66)

            EXX                         ;                           ' 4     | (78)
            wait8                       ; sync                      ' 8     |

            LD      (HL), $88           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 32 (110)
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $89           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (172)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $8A           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (234)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            EXX                         ;                           ' 4     | (238)

.wait5
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait5          ; wait while busy           ' 10    | 22 (254+6)

            LD      (HL), $2A           ;                           ' 10    |
            LD      A, (BC)             ;                           ' 7     |
            INC     BC                  ; *** sample output ****    ' 6     | 38 (44)
            RES     2, B                ;                           ' 8     |
            LD      (DE), A             ;                           ' 7     |

.wait6
            BIT     7, (HL)             ; test YM ready bit         ' 12    |
            JP      NZ, .wait6          ; wait while busy           ' 10    | 22 (66)

            EXX                         ;                           ' 4     | (78)
            wait8                       ; sync                      ' 8     |

            LD      (HL), $8C           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 32 (110)
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $8D           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (172)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            waitYMReady                 ; wait YM to be ready       ' 30    |
            LD      (HL), $8E           ; set reg num to YM         ' 10    |
            LD      A, (DE)             ; get saved value           ' 7     | 62 (234)
            INC     E                   ; next                      ' 4     |
            wait4                       ; wait YM to be ready       ' 4     |
            LD      (BC), A             ; restore state             ' 7     |

            RET                         ; done                      ' 10    | (244)


; loadPSGState
; ------------
; PSGPORT     ->  HL
; reg source  ->  DE  -> ?
;
; load the PSG env state
; 94 cycles

loadPSGState

            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | (21)
            LD      (HL), A             ; PSG restore channel 0 env ' 10    |

            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 21 (42)
            LD      (HL), A             ; PSG restore channel 1 env ' 10    |

            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 21 (63)
            LD      (HL), A             ; PSG restore channel 2 env ' 10    |

            LD      A, (DE)             ; get saved value           ' 7     |
            INC     E                   ; next                      ' 4     | 21 (84)
            LD      (HL), A             ; PSG restore channel 3 env ' 10    |

            RET                         ; done                      ' 10    | (94)


; ##############################  jump table  ################################

            BLOCK   $1600-$

            DW      com_next_frame                                                                  ; 00

            DW      com_null, com_null, com_null                                                    ; 01-03
            DW      com_null, com_null, com_null, com_null                                          ; 04-07
            DW      com_null, com_null, com_null, com_null                                          ; 08-0B
            DW      com_null, com_null, com_null, com_null                                          ; 0C-0F

            DW      com_psg_tone_w0, com_psg_tone_w1, com_psg_tone_w2, com_psg_tone_w3              ; 10-13
            DW      com_psg_tone_w4, com_psg_tone_w5, com_psg_tone_w6, com_psg_tone_w7              ; 14-17
            DW      com_psg_env_w0, com_psg_env_w1, com_psg_env_w2, com_psg_env_w3                  ; 18-1B
            DW      com_null, com_null, com_null, com_null                                          ; 1C-1F

            DW      com_ym_port0_w0, com_ym_port0_w1, com_ym_port0_w2, com_ym_port0_w3              ; 20-23
            DW      com_ym_port0_w4, com_ym_port0_w5, com_ym_port0_w6, com_ym_port0_w7              ; 24-27
            DW      com_ym_port0_w8, com_ym_port0_w9, com_ym_port0_wA, com_ym_port0_wB              ; 28-2B
            DW      com_ym_port0_wC, com_ym_port0_wD, com_ym_port0_wE, com_ym_port0_wF              ; 2C-2F

            DW      com_ym_port2_w0, com_ym_port2_w1, com_ym_port2_w2, com_ym_port2_w3              ; 30-33
            DW      com_ym_port2_w4, com_ym_port2_w5, com_ym_port2_w6, com_ym_port2_w7              ; 34-37
            DW      com_ym_port2_w8, com_ym_port2_w9, com_ym_port2_wA, com_ym_port2_wB              ; 38-3B
            DW      com_ym_port2_wC, com_ym_port2_wD, com_ym_port2_wE, com_ym_port2_wF              ; 3C-3F

            DW      com_ym_key_w0, com_ym_key_w1, com_ym_key_w2, com_ym_key_w3                      ; 40-43
            DW      com_ym_key_w4, com_ym_key_w5, com_null, com_null                                ; 44-47
            DW      com_null, com_null, com_null, com_null                                          ; 48-4B
            DW      com_null, com_null, com_null, com_null                                          ; 4C-4F

            DW      com_pcm_p0_ch0, com_pcm_p0_ch1, com_pcm_p0_ch2, com_pcm_p0_ch3                  ; 50-53
            DW      com_pcm_p1_ch0, com_pcm_p1_ch1, com_pcm_p1_ch2, com_pcm_p1_ch3                  ; 54-57
            DW      com_pcm_p2_ch0, com_pcm_p2_ch1, com_pcm_p2_ch2, com_pcm_p2_ch3                  ; 58-5B
            DW      com_pcm_p3_ch0, com_pcm_p3_ch1, com_pcm_p3_ch2, com_pcm_p3_ch3                  ; 5C-5F

            DW      com_state_w0, com_state_w1, com_state_w2, com_state_w3                          ; 60-63
            DW      com_state_w4, com_state_w5, com_state_w6, com_state_w7                          ; 64-67
            DW      com_state_w8, com_state_w9, com_state_wA, com_state_wB                          ; 68-6B
            DW      com_state_wC, com_state_wD, com_state_wE, com_state_wF                          ; 6C-6F

            DW      com_null, com_null, com_null, com_null                                          ; 70-74
            DW      com_null, com_null, com_null, com_null                                          ; 74-77
            DW      com_null, com_null, com_null, com_null                                          ; 78-7B
            DW      com_null                                                                        ; 7C

            DW      com_extra_frm                                                                   ; 7D
            DW      com_loop                                                                        ; 7E
            DW      com_end                                                                         ; 7F


            END
