Subversion Repositories svn.mios

Rev

Blame | Last modification | View Log | RSS feed

; $Id: tia_midi.inc bdupeyron.tech@gmail.com(Antichambre)
;
; MIDIbox TIA
; MIDI Interface part
;
; ==========================================================================
;
;  Copyright 1998-2006 Thorsten Klose (tk@midibox.org)
;  Licensed for personal non-commercial use only.
;  All other rights reserved.
; 
; ==========================================================================

;; --------------------------------------------------------------------------
;;  This function is called by TIA_MPROC when a complete MIDI event has been
;;  received
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_NotifyReceivedEvent

    ;; branch to appr. TIA routine depending on received event
    swapf   MIOS_PARAMETER1, W
    andlw   0x07
    JUMPTABLE_2BYTES_UNSECURE
    rgoto   TIA_MIDI_NoteOff
    rgoto   TIA_MIDI_NoteOn
    rgoto   TIA_MIDI_AfterTouch
    rgoto   TIA_MIDI_CC
    rgoto   TIA_MIDI_ProgramChange
    rgoto   TIA_MIDI_PolyAfterTouch
    rgoto   TIA_MIDI_PitchBender
    return

;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Note On event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_NoteOn
    movf    MIOS_PARAMETER3, W  ; branch to NoteOff if velocity is zero
    skpnz
    rgoto   TIA_MIDI_NoteOff
    

    
    SET_BSR TIA_BASE        ; prepare BSR for TIA register access

    BRA_IFCLR TIA_PLAY_MODE, TIA_PLAY_MODE_POLY, BANKED, TIA_MIDI_NoteOn_MonoMode
TIA_MIDI_NoteOn_PolyMode
    ;; in poly mode we only react on MIDI channel of voice 1!
    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    return
    
    ;; in poly mode: determine free voice
    movlw   0x01
    movf    TIA_V1_BASE + TIA_Vx_NOTE_STACK_0, F, BANKED
    bz  TIA_MIDI_NoteOn_Poly_Cont
    movlw   0x02
    movf    TIA_V2_BASE + TIA_Vx_NOTE_STACK_0, F, BANKED
    bz  TIA_MIDI_NoteOn_Poly_Cont
    rgoto   TIA_MIDI_NoteOn_Poly_Failed
TIA_MIDI_NoteOn_Poly_Cont
    movwf   TMP5        ; TMP5 contains the voices which should be played
    rcall   TIA_MIDI_GetAssignedKeys
    rcall   TIA_MIDI_GetAssignedKeys
    rgoto   TIA_MIDI_NoteOn_Start_Handlers

TIA_MIDI_NoteOn_MonoMode
    ;; check for the assigned MIDI channels, result in TMP5
    rcall   TIA_MIDI_GetAssignedChannels
    rcall   TIA_MIDI_GetAssignedVoices
    rcall   TIA_MIDI_GetAssignedKeys
    ;; leave routine if no voice is assigned to channel
    skpnz
    return

TIA_MIDI_NoteOn_Start_Handlers
    IRQ_DISABLE

    ;; --[ Voice 1 Handler ]--
TIA_MIDI_NoteOn_V1
    BRA_IFCLR TMP5, 0, ACCESS, TIA_MIDI_NoteOn_V1_Failed
    lfsr    FSR1, TIA_V1_BASE + TIA_Vx_NOTE_STACK_0     ; push note to stack
    rcall   TIA_MIDI_Hlp_PushNote
    BRA_IFSET WREG, 0, ACCESS, TIA_MIDI_NoteOn_V1_Failed    ; exit if note already in stack
    lfsr    FSR0, TIA_V1_BASE
    movf    MIOS_PARAMETER2, W              ; note which should be disabled
;   RCALL_IFSET TIA_PLAY_MODE, TIA_PLAY_MODE_LEGATO_OFF, BANKED, TIA_MIDI_Hlp_GateOff   ; request gate-off if !legato
    rcall   TIA_MIDI_Hlp_NoteOn             ; call note-on handler
    lfsr    FSR2, TIA_V1_BASE               ; sort notes for arpeggios
    rcall   TIA_MIDI_Arp_Sorter
TIA_MIDI_NoteOn_V1_Failed

    ;; --[ Voice 2 Handler ]--
TIA_MIDI_NoteOn_V2
    BRA_IFCLR TMP5, 1, ACCESS, TIA_MIDI_NoteOn_V2_Failed
    lfsr    FSR1, TIA_V2_BASE + TIA_Vx_NOTE_STACK_0     ; push note to stack
    rcall   TIA_MIDI_Hlp_PushNote
    BRA_IFSET WREG, 0, ACCESS, TIA_MIDI_NoteOn_V2_Failed    ; exit if note already in stack
    lfsr    FSR0, TIA_V2_BASE
    movf    MIOS_PARAMETER2, W              ; note which should be disabled
;   RCALL_IFSET TIA_PLAY_MODE, TIA_PLAY_MODE_LEGATO_OFF, BANKED, TIA_MIDI_Hlp_GateOff   ; request gate-off if !legato
    rcall   TIA_MIDI_Hlp_NoteOn             ; call note-on handler
    lfsr    FSR2, TIA_V2_BASE               ; sort notes for arpeggios
    rcall   TIA_MIDI_Arp_Sorter
TIA_MIDI_NoteOn_V2_Failed

TIA_MIDI_NoteOn_Poly_Failed

    IRQ_ENABLE
    return

;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Note Off event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_NoteOff
    SET_BSR TIA_BASE        ; prepare BSR for TIA register access

    ;; ensure that velocity is cleared
    clrf    MIOS_PARAMETER3

    BRA_IFCLR TIA_PLAY_MODE, TIA_PLAY_MODE_POLY, BANKED, TIA_MIDI_NoteOff_MonoMode
TIA_MIDI_NoteOff_PolyMode
    ;; in poly mode, handle all channels
    movlw   0x0f
    movwf   TMP5
    rgoto   TIA_MIDI_NoteOff_Start_Handlers
TIA_MIDI_NoteOff_MonoMode
    ;; check for the assigned MIDI channels, result in TMP5
    rcall   TIA_MIDI_GetAssignedChannels
    rcall   TIA_MIDI_GetAssignedVoices ; (not so optimal if split points modified during notes are played)
    rcall   TIA_MIDI_GetAssignedKeys
    ;; leave routine if no voice is assigned to channel
    skpnz
    return

TIA_MIDI_NoteOff_Start_Handlers
    IRQ_DISABLE

    ;; --[ Voice 1 Handler ]--
TIA_MIDI_NoteOff_V1
    BRA_IFCLR TMP5, 0, ACCESS, TIA_MIDI_NoteOff_V1_NotFnd
    lfsr    FSR1, TIA_V1_BASE + TIA_Vx_NOTE_STACK_0     ; pop note from stack
    movff   INDF1, TMP3                 ; save current #0 entry in TMP3 for later use
    rcall   TIA_MIDI_Hlp_PopNote
    BRA_IFSET WREG, 0, ACCESS, TIA_MIDI_NoteOff_V1_NotFnd
    lfsr    FSR0, TIA_V1_BASE
    movf    TMP3, W                     ; restore note
    rcall   TIA_MIDI_Hlp_NoteOff
    RCALL_IFSET WREG, 0, ACCESS, TIA_MIDI_Hlp_NoteOn
    lfsr    FSR2, TIA_V1_BASE               ; sort notes for arpeggios
    rcall   TIA_MIDI_Arp_Sorter
TIA_MIDI_NoteOff_V1_NotFnd

    ;; --[ Voice 2 Handler ]--
TIA_MIDI_NoteOff_V2
    BRA_IFCLR TMP5, 1, ACCESS, TIA_MIDI_NoteOff_V2_NotFnd
    lfsr    FSR1, TIA_V2_BASE + TIA_Vx_NOTE_STACK_0     ; pop note from stack
    movff   INDF1, TMP3                 ; save current #0 entry in TMP3 for later use
    rcall   TIA_MIDI_Hlp_PopNote
    BRA_IFSET WREG, 0, ACCESS, TIA_MIDI_NoteOff_V2_NotFnd
    lfsr    FSR0, TIA_V2_BASE
    movf    TMP3, W                     ; restore note
    rcall   TIA_MIDI_Hlp_NoteOff
    RCALL_IFSET WREG, 0, ACCESS, TIA_MIDI_Hlp_NoteOn
    lfsr    FSR2, TIA_V2_BASE               ; sort notes for arpeggios
    rcall   TIA_MIDI_Arp_Sorter
TIA_MIDI_NoteOff_V2_NotFnd

    IRQ_ENABLE
    return


;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a PitchBender event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_PitchBender
    SET_BSR TIA_BASE
    rlf MIOS_PARAMETER2, W
    andlw   0xfe
    xorlw   0x80
    movwf   MIOS_PARAMETER2
    
    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
TIA_MIDI_PitchBender_v1
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    rgoto TIA_MIDI_PitchBender_v2
    movf    MIOS_PARAMETER2, W
    movwf   TIA_V1_BASE + TIA_Vx_PITCHBENDER, BANKED
    rgoto   TIA_MIDI_PitchBender_End
    
TIA_MIDI_PitchBender_v2
    cpfseq  TIA_V2_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    rgoto TIA_MIDI_PitchBender_End
    movf    MIOS_PARAMETER2, W
    movwf   TIA_V2_BASE + TIA_Vx_PITCHBENDER, BANKED
TIA_MIDI_PitchBender_End
    return

;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Controller event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_CC
    SET_BSR TIA_BASE

    ;; special treatment for CC#0 (bank change)
    movf    MIOS_PARAMETER2, W
    bnz TIA_MIDI_CC_No00
TIA_MIDI_CC_00
    ;; exit if bank number >= DEFAULT_BS_KBANK_ID*4
    movlw   DEFAULT_BS_KBANK_ID*4
    cpfslt  MIOS_PARAMETER3
    return

    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    return
    
    movff   MIOS_PARAMETER3, TIA_PBANK

    ;; Int.Patch if ==0
    movf    TIA_PATCH, W
    bz      TIA_MIDI_CC_00_Ok
    
    ;; Banstick Ready
    movf    TIA_PBANK, W
    call    TIA_BANK_GetBankStickReady
    skpnz
    clrf    TIA_PATCH   ;; to Int. Patch if BS not ready
     
    ;; Banstick Size
    call    TIA_BANK_GetBankStickSize
    bnz     TIA_MIDI_CC_00_Ok
    ;; 64/128 patches
    movf    TIA_PATCH, W
    andlw   0xc0
    skpz
    clrf    TIA_PATCH   ;; to Int. Patch if >63
    
TIA_MIDI_CC_00_Ok
    call    TIA_PATCH_Init
    ;;goto  USER_DISPLAY_Init
TIA_MIDI_CC_00_End
    return


TIA_MIDI_CC_No00
    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    return

    ;; forward CC to CCIN_Set routine
    movff   MIOS_PARAMETER3, MIOS_PARAMETER1
    movf    MIOS_PARAMETER2, W
    call    TIA_CCIN_Set

    return


;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Program Change event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second  MIDI event byte in MIOS_PARAMETER2
;; --------------------------------------------------------------------------
TIA_MIDI_ProgramChange
    SET_BSR TIA_BASE

    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    return
    
    ;; Int.Patch if ==0 
    movf    MIOS_PARAMETER2, W
    bz      TIA_MIDI_ProgramChange_Ok
    
    ;; Banstick Ready
    movf    TIA_PBANK, W
    call    TIA_BANK_GetBankStickReady
    skpnz
    clrf    MIOS_PARAMETER2
     
    ;; Banstick Size
    call    TIA_BANK_GetBankStickSize
    bnz     TIA_MIDI_ProgramChange_Ok
    ;; 64/128 patches
    btfsc   MIOS_PARAMETER2, 6
    rgoto   TIA_MIDI_ProgramChange_End   
    
TIA_MIDI_ProgramChange_Ok
    movff   MIOS_PARAMETER2, TIA_PATCH
    call    TIA_PATCH_Init
    ;;goto  USER_DISPLAY_Init
TIA_MIDI_ProgramChange_End
    return

;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Poly Aftertouch event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;; --------------------------------------------------------------------------
TIA_MIDI_PolyAfterTouch
    movff   MIOS_PARAMETER2, MIOS_PARAMETER3
    rgoto   TIA_MIDI_AfterTouch

;; --------------------------------------------------------------------------
;;  This function is rcalled to forward a Aftertouch event to the synthesizer
;;  Input:
;;     o first  MIDI event byte in MIOS_PARAMETER1
;;     o second MIDI event byte in MIOS_PARAMETER2
;;     o third  MIDI event byte in MIOS_PARAMETER3
;; --------------------------------------------------------------------------
TIA_MIDI_AfterTouch
    SET_BSR TIA_BASE

    movf    MIOS_PARAMETER1, W  ; leave routine if MIDI channel doesn't match
    andlw   0x0f
    cpfseq  TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, BANKED
    return

    movf    MIOS_PARAMETER3, W
_TIA_MIDI_AfterTouch
    movwf   MIOS_PARAMETER1
    SET_BSR TIA_BASE        ; prepare BSR for TIA register access
    lfsr    FSR1, TIA_CTRL_AFTERTOUCH_BASE; prepare FSR1
    goto    TIA_CCIN_Cmd_AFTERTOUCH ; set aftertouch value


;; --------------------------------------------------------------------------
;;  help routines
;; --------------------------------------------------------------------------

    ;; ------------------------------------------------------------------
    ;; Push a note into the stack
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_PushNote
    clrf    TMP1
    ;; do nothing if note is already stored in note stack
TIA_MIDI_Hlp_PushNote_CheckLoop
    movf    TMP1, W
    movf    PLUSW1, W
    xorwf   MIOS_PARAMETER2, W
    skpnz
    rgoto   TIA_MIDI_Hlp_PushNote_Failed       ; leave note routine if note already stored
    incf    TMP1, F
    movlw   TIA_NOTE_STACK_LEN
    cpfseq  TMP1, ACCESS
    rgoto TIA_MIDI_Hlp_PushNote_CheckLoop
    
    ;; shift right note stack 
    movlw   (TIA_NOTE_STACK_LEN-2)
    movwf   TMP1
TIA_MIDI_Hlp_PushNote_ShiftLoop
    movf    TMP1, W
    movff   PLUSW1, TMP2
    incf    TMP1, W
    movff   TMP2, PLUSW1
    decf    TMP1, F
    incf    TMP1, W
    bnz TIA_MIDI_Hlp_PushNote_ShiftLoop

    ;; store new note at offset 0
    movff   MIOS_PARAMETER2, INDF1

    retlw   0x00        ; return 0x00 as error status

TIA_MIDI_Hlp_PushNote_Failed
    retlw   0x01        ; return 0x01 as error status

    ;; ------------------------------------------------------------------

    ;; ------------------------------------------------------------------
    ;; Pop a note from the stack
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_PopNote
    ; search for note entry with the same number, erase it and push the entries behind
    clrf    TMP1
TIA_MIDI_Hlp_PopNote_SearchLoop
    movf    TMP1, W
    movf    PLUSW1, W
    xorwf   MIOS_PARAMETER2, W
    bz  TIA_MIDI_Hlp_PopNote_Found
    incf    TMP1, F
    movlw   TIA_NOTE_STACK_LEN
    cpfseq  TMP1, ACCESS
    rgoto TIA_MIDI_Hlp_PopNote_SearchLoop
    rgoto   TIA_MIDI_Hlp_PopNote_Failed
TIA_MIDI_Hlp_PopNote_Found

    ;; push the entries behind the found entry
TIA_MIDI_Hlp_PopNote_ShiftLoop
    incf    TMP1, W
    movff   PLUSW1, TMP2
    movf    TMP1, W
    movff   TMP2, PLUSW1
    incf    TMP1, F
    movlw   TIA_NOTE_STACK_LEN
    cpfseq  TMP1, ACCESS
    rgoto TIA_MIDI_Hlp_PopNote_ShiftLoop
    ;; clear the last entry
    movlw   TIA_NOTE_STACK_LEN-1
    clrf    PLUSW1
    retlw   0x00        ; return with 0x00: note deleted from stack

TIA_MIDI_Hlp_PopNote_Failed
    retlw   0x01        ; return with 0x01: note not found in stack

    ;; ------------------------------------------------------------------

    ;; ------------------------------------------------------------------
    ;; Note On help function
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_NoteOn
;   BRA_IFCLR TIA_PLAY_MODE, TIA_PLAY_MODE_ONLY_WT_OFF, BANKED, TIA_MIDI_Hlp_NoteOn_NoNewNote
TIA_MIDI_Hlp_NoteOn_NewNote
    movlw   TIA_Vx_NOTE
    movff   PLUSW0, TABLAT
    movff   INDF1, PLUSW0
TIA_MIDI_Hlp_NoteOn_NoNewNote

    ;; if sus-key enabled, skip enable portamento when only one key pressed
;   BRA_IFCLR TIA_PLAY_MODE, TIA_PLAY_MODE_SUS_KEY, BANKED, TIA_MIDI_Hlp_NoteOn_SusKeyPor

    ;; special case: don't disable portamento on a note off event
;   movf    MIOS_PARAMETER3, W
;   bz  TIA_MIDI_Hlp_NoteOn_SusKeySkip

;   movlw   TIA_Vx_STAT
;   bcf PLUSW0, Vx_STAT_PORTA_ENABLE

;   movlw   0x01
;   movf    PLUSW1, W
;   bz  TIA_MIDI_Hlp_NoteOn_SusKeyNoPor

TIA_MIDI_Hlp_NoteOn_SusKeyPor
    ;BRA_IFSET TIA_SE_OPTION, SE_OPTION_ENV2PORTA, BANKED, TIA_MIDI_Hlp_NoteOn_SusKeyPor_NC
    ;; enable portamento if rate is > 0

    movlw   TIA_Vx_PORTA_RATE
    movf    PLUSW0, W
    bz  TIA_MIDI_Hlp_NoteOn_SusKeyNoPor
TIA_MIDI_Hlp_NoteOn_SusKeyPor_NC
    movlw   TIA_Vx_STAT
    bsf PLUSW0, Vx_STAT_PORTA_ENABLE

    ;; store current frequency in TIA_Vx_PORTA_FRQ_L

    movlw   TIA_Vx_FRQ_L
    movff   PLUSW0, TMP1
    movlw   TIA_Vx_FRQ_H
    movff   PLUSW0, TMP2    

    movlw   TIA_Vx_PORTA_FRQ_L
    movff   TMP1, PLUSW0
    movlw   TIA_Vx_PORTA_FRQ_H
    movff   TMP2, PLUSW0   
    
    movlw   TIA_Vx_PORTA_CTR_L 
    clrf    PLUSW0
    movlw   TIA_Vx_PORTA_CTR_H 
    clrf    PLUSW0

    ;; reset Porta counter if constant time flag enabled
    ;movlw  TIA_Vx_MODE
    ;CALL_IFSET PLUSW0, Vx_MODE_PORTA_CONST, ACCESS, TIA_SW_Hlp_PortaCTR_Reset

TIA_MIDI_Hlp_NoteOn_SusKeyNoPor
;TIA_MIDI_Hlp_NoteOn_SusKeySkip

    ;; always re-init arpeggiator (in mono as well as in legato mode)
    movlw   TIA_Vx_ARP_CTR
    clrf    PLUSW0
    movlw   TIA_Vx_ARP_NOTE_NUMBER
    clrf    PLUSW0      ; (next increment will play the second note)

    ;; skip the rest if legato mode and current note is first note
;   BRA_IFSET TIA_PLAY_MODE, TIA_PLAY_MODE_LEGATO_OFF, BANKED, TIA_MIDI_Hlp_NoteOn_TrgGateNL
;   movf    MIOS_PARAMETER2, W
;   cpfseq  INDF1, ACCESS
;   rgoto TIA_MIDI_Hlp_NoteOn_TrgGateLSkp
;   movlw   0x01
;   movf    PLUSW1, W
;   bnz TIA_MIDI_Hlp_NoteOn_TrgGateLSkp
;TIA_MIDI_Hlp_NoteOn_TrgGateNL

    ;; request gate bit
    rcall   TIA_MIDI_Hlp_GateOn


    ;; ---[ END handle velocity ]---
    
    ;; ---[ BEGIN handle velocity ]---
    
    movff   MIOS_PARAMETER1, TMP1   ; store MIOS_PARAMETER1
    movff   MIOS_PARAMETER2, TMP2   ; store MIOS_PARAMETER2
    movf    MIOS_PARAMETER3, W  ; copy velocity value to MIOS_PARAMETER1
    bz  TIA_MIDI_Hlp_NoteOn_NoVel; no velocity on note off!
    movwf   MIOS_PARAMETER1
    movlw   TIA_Vx_LAST_VEL 
    movff   MIOS_PARAMETER3, PLUSW0   
    SET_BSR TIA_BASE        ; prepare BSR for TIA register access
    lfsr    FSR1, FSR0
    call    TIA_CCIN_Cmd_VELOCITY_SkpCopy   ; set velocity value
    movff   TMP1, MIOS_PARAMETER1   ; restore MIOS_PARAMETER1
    movff   TMP2, MIOS_PARAMETER2   ; restore MIOS_PARAMETER2
    ;; ---[ END handle velocity ]---



TIA_MIDI_Hlp_NoteOn_NoVel

TIA_MIDI_Hlp_NoteOn_TrgGateLSkp
    return


    ;; ------------------------------------------------------------------
    ;; Note Off help function
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_NoteOff
    ;; last note number of #0 (before pop) in WREG!

    ;; if not in legato mode and current note-off number equal to last entry #0: gate off
;   BRA_IFCLR TIA_PLAY_MODE, TIA_PLAY_MODE_LEGATO_OFF, BANKED, TIA_MIDI_Hlp_NoteOff_NoGOff
    cpfseq  MIOS_PARAMETER2, ACCESS
    rgoto TIA_MIDI_Hlp_NoteOff_End
    rcall   TIA_MIDI_Hlp_GateOff
TIA_MIDI_Hlp_NoteOff_NoGOff
    ;; ------------------------------------------------------------------

    ;; if still note available, play new note in NoteOn Section
    movf    INDF1, W
    skpz
    retlw   0x01        ; return, request Note On!

    ;; else request gate clear bit
    rcall   TIA_MIDI_Hlp_GateOff
TIA_MIDI_Hlp_NoteOff_End
    retlw   0x00        ; return, request NO Note On!

    ;; ------------------------------------------------------------------

    ;; ------------------------------------------------------------------
    ;; Gate On help function
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_GateOn
    clrc
    movlw   TIA_Vx_NOTE_DELAY
    rlf PLUSW0, W
    movwf   TABLAT
    movlw   TIA_Vx_NOTE_DELAY_CTR
    movff   TABLAT, PLUSW0

    movlw   TIA_Vx_STAT
    ;btfsc  TIA_PLAY_MODE, TIA_PLAY_MODE_ONLY_WT_OFF, BANKED
    bsf PLUSW0, Vx_STAT_GATE_SET_REQ
    bsf PLUSW0, Vx_STAT_VOICE_ACTIVE

    ;; reset wavetable handler
    movlw   TIA_Vx_WT_STATE
    bsf PLUSW0, WT_STATE_RESET

    return

    ;; ------------------------------------------------------------------
    ;; Gate Off help function
    ;; ------------------------------------------------------------------
TIA_MIDI_Hlp_GateOff
    clrc
    movlw   TIA_Vx_NOTE_DELAY
    rlf PLUSW0, W
    movwf   TABLAT
    movlw   TIA_Vx_NOTE_DELAY_CTR
    movff   TABLAT, PLUSW0

    movlw   TIA_Vx_STAT
    bcf PLUSW0, Vx_STAT_GATE_SET_REQ
    bsf PLUSW0, Vx_STAT_GATE_CLR_REQ
    bcf PLUSW0, Vx_STAT_VOICE_ACTIVE
    return

    ;; ------------------------------------------------------------------
    ;; for Note On/Note Off in Mono mode
    ;; MIDI channel in MIOS_PARAMETER1[0..3]
    ;; result in TMP5
TIA_MIDI_GetAssignedChannels
    clrf    TMP5        ; TMP5 contains the voices which should be played
    
    movf    MIOS_PARAMETER1, W
    andlw   0x0f
    xorwf   TIA_V1_BASE + TIA_Vx_MIDI_CHANNEL, W, BANKED
    skpnz
    bsf TMP5, 0     ; play voice 1

    movf    MIOS_PARAMETER1, W
    andlw   0x0f
    xorwf   TIA_V2_BASE + TIA_Vx_MIDI_CHANNEL, W, BANKED
    skpnz
    bsf TMP5, 1     ; play voice 2
    
   
    movf    TMP5, W
    return

    ;; ------------------------------------------------------------------
    ;; for Note On/Off in Mono mode
    ;; note number in MIOS_PARAMETER2
    ;; result will be ANDed to TMP5 --- TIA_MIDI_GetAssignedChannels should be called before!
TIA_MIDI_GETASSIGNEDVOICEx MACRO FLAG_Vx, TIA_Vx_BASE
    LOCAL   TIA_MIDI_GETASSIGNEDVOICEx_LOk
    LOCAL   TIA_MIDI_GETASSIGNEDVOICEx_LOff
    LOCAL   TIA_MIDI_GETASSIGNEDVOICEx_End

    ;; handle split points
    movf    TIA_Vx_BASE + TIA_Vx_SPLIT_LOWER, W, BANKED ; (don't split if 0)
    iorwf   TIA_Vx_BASE + TIA_Vx_SPLIT_UPPER, W, BANKED
    bz  TIA_MIDI_GETASSIGNEDVOICEx_End
    
    movf    TIA_Vx_BASE + TIA_Vx_SPLIT_LOWER, W, BANKED
    cpfslt  MIOS_PARAMETER2, ACCESS
    rgoto TIA_MIDI_GETASSIGNEDVOICEx_LOk
    rgoto   TIA_MIDI_GETASSIGNEDVOICEx_LOff

TIA_MIDI_GETASSIGNEDVOICEx_LOk
    movf    TIA_Vx_BASE + TIA_Vx_SPLIT_UPPER, W, BANKED
    cpfsgt  MIOS_PARAMETER2, ACCESS
    rgoto TIA_MIDI_GETASSIGNEDVOICEx_End
TIA_MIDI_GETASSIGNEDVOICEx_LOff
    bcf TMP5, FLAG_Vx       ; don't play voice
TIA_MIDI_GETASSIGNEDVOICEx_End
    ENDM

TIA_MIDI_GetAssignedVoices
    TIA_MIDI_GETASSIGNEDVOICEx 0, TIA_V1_BASE
    TIA_MIDI_GETASSIGNEDVOICEx 1, TIA_V2_BASE
    movf    TMP5, W
        return
    
    ;; ------------------------------------------------------------------
    ;; for Note On/Off in Mono mode
    ;; note number in MIOS_PARAMETER2
    ;; result will be ANDed to TMP5 --- TIA_MIDI_GetAssignedChannels should be called before!
TIA_MIDI_GETASSIGNEDKEYx MACRO FLAG_Vx, TIA_Vx_BASE
    LOCAL   TIA_MIDI_GETASSIGNEDKEYx_LOk
    LOCAL   TIA_MIDI_GETASSIGNEDKEYx_LOff
    LOCAL   TIA_MIDI_GETASSIGNEDKEYx_End

    btfsc   TIA_Vx_BASE + TIA_Vx_MODE, Vx_MODE_KEY_EXTENDED, BANKED
    rgoto   TIA_MIDI_GETASSIGNEDKEYx_End
    
    movf    TIA_Vx_BASE + TIA_Vx_KEY_OFFSET, W, BANKED
    cpfslt  MIOS_PARAMETER2, ACCESS
    rgoto TIA_MIDI_GETASSIGNEDKEYx_LOk
    rgoto   TIA_MIDI_GETASSIGNEDKEYx_LOff

TIA_MIDI_GETASSIGNEDKEYx_LOk
    movlw   0x1f
    cpfsgt  TIA_Vx_BASE + TIA_Vx_KEY_LENGTH, BANKED
    movf    TIA_Vx_BASE + TIA_Vx_KEY_LENGTH, W, BANKED
    addwf   TIA_Vx_BASE + TIA_Vx_KEY_OFFSET, W, BANKED
    cpfsgt  MIOS_PARAMETER2, ACCESS
    rgoto TIA_MIDI_GETASSIGNEDKEYx_End
TIA_MIDI_GETASSIGNEDKEYx_LOff
    bcf TMP5, FLAG_Vx       ; don't play voice
TIA_MIDI_GETASSIGNEDKEYx_End
    ENDM

TIA_MIDI_GetAssignedKeys
    TIA_MIDI_GETASSIGNEDKEYx 0, TIA_V1_BASE
    TIA_MIDI_GETASSIGNEDKEYx 1, TIA_V2_BASE
    movf    TMP5, W
        return    

    
    ;; ------------------------------------------------------------------
    ;; arpeggiator sorter
    ;; expecting base pointer to voice record in FSR2
TIA_MIDI_Arp_Sorter
    ;; TIA_Vx_ARP_NOTE_0 -> FSR0
    movff   FSR2H, FSR0H
    movf    FSR2L, W
    addlw   TIA_Vx_ARP_NOTE_0
    movwf   FSR0L

    ;; TIA_Vx_NOTE_STACK_0 -> FSR1
    movff   FSR2H, FSR1H
    movf    FSR2L, W
    addlw   TIA_Vx_NOTE_STACK_0
    movwf   FSR1L

    movff   FSR0L, TMP1 ; save pointer to ARP_NOTE_0 in TMP1
    ;; clear all current entries
    clrf    POSTINC0    ; (TIA_Vx_ARP_NOTE_0)
    clrf    POSTINC0    ; (TIA_Vx_ARP_NOTE_1)
    clrf    POSTINC0    ; (TIA_Vx_ARP_NOTE_2)
    clrf    POSTINC0    ; (TIA_Vx_ARP_NOTE_3)
    movff   TMP1, FSR0L ; restore pointer to ARP_NOTE_0 from TMP1

    movf    INDF1, W    ; (TIA_Vx_NOTE_STACK_0)
    bz  TIA_MIDI_Arp_Sorter_End

    movf    POSTINC1, W ; (TIA_Vx_NOTE_STACK_0)
    rcall   TIA_MIDI_ARP_Sorter_Add
    movf    POSTINC1, W ; (TIA_Vx_NOTE_STACK_1)
    rcall   TIA_MIDI_ARP_Sorter_Add
    movf    POSTINC1, W ; (TIA_Vx_NOTE_STACK_2)
    rcall   TIA_MIDI_ARP_Sorter_Add
    movf    POSTINC1, W ; (TIA_Vx_NOTE_STACK_3)
    rcall   TIA_MIDI_ARP_Sorter_Add

    ;; if rate is > 0, and arp has been reset: copy first arp note into TIA_Vx_NOTE
    movlw   TIA_Vx_ARP_RATE
    movf    PLUSW2, W
    bz  TIA_MIDI_Arp_Sorter_End
    movlw   TIA_Vx_ARP_CTR
    movf    PLUSW2, W
    bnz TIA_MIDI_Arp_Sorter_End
    movlw   TIA_Vx_ARP_NOTE_NUMBER
    movf    PLUSW2, W
    bnz TIA_MIDI_Arp_Sorter_End

    movlw   TIA_Vx_ARP_NOTE_0
    movff   PLUSW2, TABLAT
    movlw   TIA_Vx_NOTE
    movff   TABLAT, PLUSW2

TIA_MIDI_Arp_Sorter_End
    return

;; ---
    ;; add to ARP note buffer, sort automatically from lowest to highest note
TIA_MIDI_ARP_Sorter_Add
    skpnz           ; only add notes > 0
    return

    movwf   TMP1        ; store new note number in TMP2
    clrf    TMP2        ; TMP2 used as loop counter
TIA_MIDI_ARP_Sorter_Loop
    movf    TMP2, W
    movf    PLUSW0, W
    bz  TIA_MIDI_ARP_Sorter_Push; the fourth note will ever be pushed as the appr. byte is zero
    subwf   TMP1, W
    bnc TIA_MIDI_ARP_Sorter_Push
    incf    TMP2, F
    BRA_IFCLR TMP2, 2, ACCESS, TIA_MIDI_ARP_Sorter_Loop
    return          ; this case never happens

TIA_MIDI_ARP_Sorter_Push
    movf    TMP2, W     ; fourth note: no shift required
    xorlw   0x03
    bz  TIA_MIDI_ARP_Sorter_PushN
    movlw   0x02
    movwf   TMP3
TIA_MIDI_ARP_Sorter_PushL
    movf    TMP3, W
    movff   PLUSW0, TMP4
    addlw   1
    movff   TMP4, PLUSW0
    movf    TMP2, W
    xorwf   TMP3, W
    bz  TIA_MIDI_ARP_Sorter_PushN
    decf    TMP3, F
    rgoto   TIA_MIDI_ARP_Sorter_PushL

TIA_MIDI_ARP_Sorter_PushN
    movf    TMP2, W
    movff   TMP1, PLUSW0
    return

Generated by GNU enscript 1.6.4.