Sona 0.50 Source

Sona 0.50/src-z80/fm_load.z80

;****************************************************************************
; LoadFmOp [SONAOP_LOAD_FM1,FM2,FM3,FM3SP,FM4,FM5,FM6]
; Processes the stream opcode for loading a FM instrument.
;----------------------------------------------------------------------------
; input b ..... channel ID (bits 2-0 only)
; input c ..... current stream bank
; input hl .... current stream address
;----------------------------------------------------------------------------
; output c .... new stream bank
; output hl ... new stream address
;----------------------------------------------------------------------------
; modifies .... a,bc,de,hl,a'
;****************************************************************************

LoadFmOp:
    PollPcm
    
    ld      a, b                        ; Get channel ID
    and     $07
    
    cp      $03                         ; Was it ch3 special? (you should use
    jr      nz, LoadFmOpChOk            ; ch3 normal instead, so let's fix it
    dec     a                           ; before continuing)
LoadFmOpChOk:
    
    ld      b, a                        ; Check if we own the channel (we
    ex      af, af'                     ; need to do this after the above
    CheckOwner                          ; or it'll be wrong if somebody used
                                        ; FM3 special instead of FM3 normal)
    
    call    ReadInstrumentId            ; Get instrument ID
    
    JumpIfSfx LoadFmOpNotBgm            ; If it's a BGM keep track of the
    ex      af, af'                     ; instrument ID in case we need to
    ld      d, BgmFmInstr>>8            ; restore it after a SFX stomps on
    add     a, BgmFmInstr&$FF           ; the channel
    ld      e, a                        ;
    sub     BgmFmInstr&$FF              ; TO-DO: is there a cleaner way to
    ex      de, hl                      ; do this? (this block of code needs
    ld      (hl), b                     ; to leave a',c,hl intact)
    ex      de, hl
    ex      af, af'
LoadFmOpNotBgm:
    
    RetIfNotOwner                       ; Don't load the new instrument
    ex      af, af'                     ; into the YM2612 if we don't own
                                        ; this channel
    
    push    bc                          ; Save stream position, since these
    push    hl                          ; registers will get overwritten
    
    call    LoadFmInstr                 ; Load the instrument
    
    pop     hl                          ; Restore stream position
    pop     bc
    
    ret                                 ; End of subroutine

;****************************************************************************
; LoadFmInstr
; Loads a FM instrument.
;----------------------------------------------------------------------------
; input a .... channel ID (0,1,2,4,5,6)
; input b .... instrument ID (0..255)
;----------------------------------------------------------------------------
; modifies ... a,bc,de,hl,a',iy
;              writes into ScratchBuf
;****************************************************************************

LoadFmInstr:
    ex af, af'
    PollPcm
    
    ld      h, InstrList>>8             ; Retrieve instrument addr+bank
    ld      l, b                        ; into c/hl
    ld      e, (hl)
    inc     h
    ld      d, (hl)
    inc     h
    ld      c, (hl)
    ex      de, hl
    
    PollPcm
    
    ex      af, af'                     ; Stop the channel now while we have
    ld      b, a                        ; a chance (since all registers this
    ex      af, af'                     ; clobbers are free here)
    ld      a, b
    call    StopFmChannel
    
    PollPcm
    ex      af, af'
    
    ; a = channel ID
    ; c = instrument bank
    ; hl = instrument address
    
    ld      b, a                        ; Determine which YM2612 bank
    and     1<<2                        ; to write to later
    rrca                                ; ch1~ch3 = YmAddr1/YmData1
    ld      (LoadFmInstr_Base), a       ; ch4~ch6 = YmAddr2/YmData2
    
    ld      a, b                        ; Determine register offset
    and     $03                         ; ch1/ch4 = +0
    ld      (LoadFmInstr_Offset), a     ; ch2/ch5 = +1
                                        ; ch3/ch6 = +2
    
    ld      a, b                        ; Compute pointer to variable where
    add     a, RealFmVol&$FF            ; the volume to load with is stored
    ld      (LoadFmInstr_RealVolPtr), a ; (so instrument is loaded with the
                                        ; correct volume instead of loudest)
    
    PollPcm
    
    ld      a, b                        ; The algorithm and TL values are
    add     a, a                        ; needed to do volume math, so we
    add     a, a                        ; need to store their values in a
    add     a, b                        ; buffer.
    add     a, FmVolumeRegs&$FF         ; Determine the pointer to the
    ld      iyh, FmVolumeRegs>>8        ; fields for this channel.
    ld      iyl, a
    
    PollPcm
    
    ; c = instrument bank
    ; hl = instrument address
    
    ld      a, SIZEOF_FMINSTR           ; Load entire FM instrument into the
    ld      de, ScratchBuf              ; scratch buffer (probably not the
                                        ; fastest method, but makes things
LoadFmInstr_ReadLoop:                   ; easier and we need to poke at some
    ex      af, af'                     ; of the data too)
    ReadRomByte                         ;
    ld      a, b                        ; a = loop counter
    ld      (de), a                     ; b = byte being read
    inc     e                           ; c = current bank
    ex      af, af'                     ; hl = current address
    dec     a                           ; de = pointer to buffer
    jp      nz, LoadFmInstr_ReadLoop
    
    PollPcm
    
LoadFmInstr_RealVolPtr: equ $+1
    ld      a, (RealFmVol)              ; Read current volume of the channel
    ld      l, a                        ; so the instrument is loaded with
                                        ; the correct TL values
    
    ld      a, (LoadFmInstr_Algo)       ; Retrieve the instrument's
    and     $07                         ; algorithm (and remember to mask
                                        ; away the feedback value! yikes)
    
    ld      bc, (LoadFmInstr_TL_S1)
    ld      de, (LoadFmInstr_TL_S2)
    
    ld      (iy+FMVOLREG_ALGO), a       ; Store these values where the FM
    ld      (iy+FMVOLREG_TL_S1), c      ; volume routines can find them for
    ld      (iy+FMVOLREG_TL_S2), e      ; adjusting later
    ld      (iy+FMVOLREG_TL_S3), b
    ld      (iy+FMVOLREG_TL_S4), d
    
    ex      af, af'
    PollPcm
    ex      af, af'
    
    call    AdjustFmTL                  ; Apply the channel's attenuation
    ld      (LoadFmInstr_TL_S1), bc     ; to our copy of the instrument data
    ld      (LoadFmInstr_TL_S2), de     ; so it takes effect immediately
                                        ;
                                        ; If this looks "wrong", remember
                                        ; that register order is S1, S3, S2,
                                        ; S4 so it's really loading S1/S3
                                        ; into BC and S2/S4 into DE.
    
    PollPcm
    
LoadFmInstr_Base: equ $+2
    ld      iy, YmAddr1
LoadFmInstr_Offset: equ $+1
    ld      a, $00                      ; First write the algorithm/feedback
    add     a, YMREG_ALGO               ; register since it's out of sequence
    ld      (iy+0), a                   ; compared to the rest
    ld      a, (LoadFmInstr_Algo)       ;
LoadFmInstr_LoadCount: equ $+1          ; We also set up the loop counter and
    ld      b, SIZEOF_FMINSTR-1         ; pointer for the loop below to spend
    ld      hl, ScratchBuf+1            ; some time between the writes
    ld      (iy+1), a                   ;
                                        ; The loop count depends on whether
                                        ; SSG-EG is enabled or not.
    
    PollPcm
    
    ld      a, (LoadFmInstr_Offset)     ; Remaining instruments go from +$30
    add     a, YMREG_MULDT              ; onwards in steps of 4, so loop
                                        ; through all of them and write them
LoadFmInstr_WriteLoop:                  ; to the YM2612
    ld      c, (hl)                     ;
    ld      (iy+0), a                   ; a = register number
    ld      (iy+1), c                   ; b = loop counter
    push    hl                          ; c = scratch
    pop     hl                          ; hl = pointer to buffer
    add     a, 4
    inc     l
    djnz    LoadFmInstr_WriteLoop
    
    PollPcm
    
    ret                                 ; End of subroutine