               INCLUDE HPFIXUPS
               TITLE "NON_PRIO"
***************************************************************************************************
***************************************************************************************************
***                                                                                             ***
***            NON_PRIO - NON-PRIORITY VOICE PLAYBACK VECTOR ROUTINES                           ***
***                                                                                             ***
***************************************************************************************************
***************************************************************************************************
;
NEG_EXT        EQU     0FFFFFF00H      ;USE IN MOVEQ INSTRUCTIONS WHERE ARG IS
                                       ;80H OR LARGER - HP XASM DOES NOT HANDLE
                                       ;SIGN-EXTEND CORRECTLY.  COSMETIC, KEEPS
                                       ;ACTUAL BYTE VALUE CLEARER.
;
               INCLUDE EQUATES         ;HDW ADDR AND CONSTANT DEFS, ABS_SHORT DIRECTIVE.
;
               INCLUDE S_BLK_EQU       ;SOUND BLOCK EQUATES/OFFSETS
;
               GLB     VCA_FWD_ATTACK,VCA_REV_ATTACK,VCA_SUSTAIN
               GLB     VCA_RELEASE,VCA_OFF
               GLB     VCF_ATTACK,VCF_RELEASE,VCF_SUSTAIN
               GLB     VCF_MAINTAIN,VCF_OFF
               GLB     BEND_SUSTAIN,BEND_RELEASE,PITCH_OUTPUT
;
               EXTERNAL  STOP_SUSTAIN_LOOP
               EXTERNAL  NOTHINGNESS
               EXTERNAL  MIN_TUNE_INDEX,MAX_TUNE_INDEX,ROOT_TUNE
;
               EXTERNAL  V_VCF_VECTOR       ;VOICE BLOCK ELEMENTS.
               EXTERNAL  V_CUR_VCF
               EXTERNAL  V_F_SUS_LEVEL
               EXTERNAL  V_F_SUS_TIME
               EXTERNAL  V_F_ATT_RATE
               EXTERNAL  V_F_REL_RATE
               EXTERNAL  V_CHIP_ADDR
               EXTERNAL  V_CHIP_MISC
               EXTERNAL  V_IDENTITY
               EXTERNAL  V_THIS_VOICE
               EXTERNAL  V_CUR_VCA
               EXTERNAL  V_A_SUS_LEVEL
               EXTERNAL  V_A_ATT_RATE
               EXTERNAL  V_A_ATT_TIME
               EXTERNAL  V_VCA_VECTOR
               EXTERNAL  V_A_REL_RATE
               EXTERNAL  V_A_REL_TIME
               EXTERNAL  V_PRIO_TIME
               EXTERNAL  V_A_SUS_TIME
               EXTERNAL  V_N_OFF_TIME
               EXTERNAL  V_F_CUTOFF
               EXTERNAL  V_PITCH_PAN
               EXTERNAL  V_CUR_PITCH
               EXTERNAL  V_BEND_LEVEL
               EXTERNAL  V_FIN_PITCH
               EXTERNAL  V_BEND_RATE
               EXTERNAL  V_BEND_VECTOR
;
               EXTERNAL  IDLE_DYNAMIC       ;RAM, IF YOU WILL.
               EXTERNAL  ACTIVE_DYNAMIC
               EXTERNAL  PRIO_DYNAMIC
               EXTERNAL  CUR_SQ_DYN_ON
               EXTERNAL  CUR_SQ_FIX_ON
               EXTERNAL  CHIPS_TO_ZERO
               EXTERNAL  VOICES_TO_OFF
               EXTERNAL  VOICES_TO_REL
               EXTERNAL  REAL_TIME
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
; GENERAL NOTES:
;
;    NON-PRIORITY VECTORS ARE CALLED WITH:
;              A0 POINTING TO VOICE COMMON BLOCK.
;              A1 POINTING TO ACTIVE VOICE SUB-BLOCK.
;              A2 POINTING TO SAMPLE/HOLD CHANNELS FOR CURRENT VOICE.
;    PLEASE - DON'T ASSUME ANYTHING ELSE - EVENTUALLY YOU WILL COME TO
;    REGRET HAVING DONE SO. REGISTERS LISTED ABOVE MUST NOT BE TAMPERED
;    WITH, ALL OTHERS ARE OPEN GAME.
;
;    OUTPUT TO S/H CHANNELS OR SOUND CHIP REGISTERS HAPPENS WITHIN EACH
;    ROUTINE - NO DISPATCH CENTRAL HERE.
;
;    VCA AND VCF ROUTINES DO S/H OUTPUT AS FIRST STEP BASED ON ENVELOPE
;    VALUES AT CALL TIME, THEN UPDATE VALUES FOR NEXT TIME AND RETURN -
;    NO MAJOR REASON FOR THIS, BUT PRIORITY START_ENVELOPES ROUTINE
;    TAKES ADVANTAGE OF IT TO OUTPUT INITIAL VCA/VCF ENVELOPE VALUES.
;    FOR THIS REASON IT IS NOT SAFE TO ASSUME THAT ROTATING_VOICE VALUE
;    IS EQUAL TO THE INDEX OF THE VOICE BEING SERVICED HERE - SO DON'T.
;
;    FOR VCA CURVES, TABLE FACTORS ALWAYS VALID -
;    HOWEVER, ATTACK PHASE IS BYPASSED AT MAX ATTACK RATE.
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCA_FWD_ATTACK
;
;04DEC               MOVE.L  #0FFF00000H,D0       ;FULL-SCALE ENVELOPE PEAK VALUE = 0FFFH (12 BITS).
               MOVE.L  #0FAE00000H,D0       ;FULL-SCALE ENVELOPE PEAK VALUE = 0FFFH (12 BITS),
                                            ;ADJUSTED FOR VCA FLOOR LEVEL (510H).
;04DEC;
               MOVE.L  V_CUR_VCA(A1),D1     ;CURRENT ENVELOPE POINT BEFORE LEVEL SCALING.
               SUB.L   D1,D0                ;FWD ATTACK IS EXPONENTIAL DECAY TOWARDS PEAK -
                                            ;D0 NOW CONTAINS THE EXPONENTIAL COMPONENT ONLY.
               SWAP    D1                   ;USE ONLY M.S.WORD FOR OUTPUT.
               MULU    V_A_SUS_LEVEL(A1),D1 ;SCALE ENVELOPE POINT BY COMBINED LEVEL FACTOR.
               BEQ.S   VCA_F_ATT_40         ;IF RESULT = 0, THEN V_A_SUS_LEVEL = 0 - DON'T BOTHER
                                            ;WITH VCA OUTPUT (SHOULD BE ZERO SINCE WE'LL ONLY COME
                                            ;THROUGH THIS ONCE), GO HANG THE VOICE UP.
               SWAP    D1                   ;M.S.PRODUCT IS THE PART WE WANT -
;04DEC;
               ADD     #0510H,D1            ;BUMP UP PER VCA FLOOR LEVEL.
;04DEC;
               MOVE    D1,2(A2)             ;PUT IT OUT TO VCA S/H.
;
               SUBQ    #1,V_A_ATT_TIME(A1)  ;UPDATE REMAINING ATTACK PHASE TIME - USED FOR DYNAMIC-
                                            ;ASSIGN VOICE-STEAL TEST, TO FIND TIME-TO-COMPLETION
                                            ;FOR THIS VOICE (IF IT IS NOT STOLEN).
                                            ;NOTE: VALID VALUES ARE IN THE RANGE 0-16383 (130 SEC),
                                            ;TIME-REMAINING COMPUTATION INGORES NEGATIVE VALUES.
;
                                            ;COMPUTE NEXT ENVELOPE POINT:
               LEA     V_A_ATT_RATE(A1),A3  ;FETCH ATTACK RATE (TWO WORDS LONG) -
               MOVEM   (A3)+,D1-D2
               MOVE    D1,D3                ;WE'LL NEED AN EXTRA COPY OF M.S. RATE WORD.
               MULU    D0,D3                ;L.S.LEVEL * M.S.RATE.
               SWAP    D0                   ;SWAP M.S.LEVEL INTO MULTIPLY POSITION.
               MULU    D0,D1                ;M.S.LEVEL * M.S. RATE.
               MULU    D0,D2                ;M.S.LEVEL * L.S.RATE.
                                            ;(WE DON'T BOTHER WITH L.S.LEVEL * L.S.RATE).
               CLR     D2                   ;YET MORE KLUDGERY - DROP L.S.WORDS OF BOTH LOW-ORDER
               SWAP    D2                   ;PRODUCTS, THEN ADD THEM TO M.S.PRODUCT - NO ROUNDING.
               CLR     D3
               SWAP    D3
               ADD.L   D2,D1                ;COMBINE THE PARTIAL PRODUCTS WITH M.S.PRODUCT.
               ADD.L   D3,D1                ;SO, THIS BE IT - EXPONENTIAL COMPONENT, THAT IS -
;
;04DEC               MOVE.L  #0FFF00000H,D0       ;CONVERT IT TO ENVELOPE POINT FORMAT,
;04DEC;
               MOVE.L  #0FAE00000H,D0       ;CONVERT IT TO ENVELOPE POINT FORMAT,
                                            ;WITH VCA FLOOR LEVEL ADJUSTMENT INCLUDED.
;04DEC;
               SUB.L   D1,D0
               MOVE.L  D0,V_CUR_VCA(A1)     ;SAVE IT FOR NEXT TIME (ASSUMING THERE IS A NEXT TIME).
               SWAP    D1                   ;NOW, BACK INTO 12-BIT LAND TO SEEK ENVELOPE DEATH:
;
;04DEC               CMP     #300H,D1             ;HAS EXPONENTIAL COMPONENT DROPPED OUT OF SIGHT YET?
;04DEC;
               CMP     #010H,D1             ;HAS EXPONENTIAL COMPONENT DROPPED OUT OF SIGHT YET?
;04DEC;
               BCC.S   VCA_F_ATT_20         ;BRANCH IF NOT - WE'RE STILL ATTACKING.
               MOVE.W  #VCA_SUSTAIN,V_VCA_VECTOR(A1)  ;ELSE - BOOM! WE'RE IN SUSTAIN PHASE.
               CLR     V_A_ATT_TIME(A1)     ;MAKE SURE NO ATTACK TIME APPEARS TO BE LINGERING ON.
VCA_F_ATT_20
               MOVE    V_N_OFF_TIME(A1),D0  ;IS A DELAYED SELF-GATE-OFF TIMING OUT?
               BEQ.S   VCA_F_ATT_EXIT       ;BRANCH IF NOT - EXIT.
               CMP     REAL_TIME,D0         ;IF SO, DID WE PASS GATE-OFF TIME YET?
               BCC.S   VCA_F_ATT_EXIT       ;BRANCH IF NOT, ELSE ....
VCA_F_ATT_40
               CLR     V_N_OFF_TIME(A1)     ;VOICE GATING ITSELF OFF - STOP THE TIMER,
               MOVE    V_THIS_VOICE(A0),D0  ;FETCH INDEX OF THIS VOICE,
               BSET    D0,VOICES_TO_OFF     ;POST A REQUEST TO GATE THIS VOICE OFF FROM BACKGROUND.
VCA_F_ATT_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCA_REV_ATTACK
               MOVE.L  V_CUR_VCA(A1),D0     ;CURRENT ENVELOPE POINT BEFORE LEVEL SCALING.
               MOVE.L  D0,D1                ;COPY FOR LEVEL-SCALE, OUTPUT.
               SWAP    D1                   ;USE ONLY M.S.WORD FOR OUTPUT.
               MULU    V_A_SUS_LEVEL(A1),D1 ;SCALE BY COMBINED LEVEL FACTOR.
               BEQ.S   VCA_R_ATT_60         ;IF RESULT = 0, THEN V_A_SUS_LEVEL = 0 - DON'T BOTHER
                                            ;WITH VCA OUTPUT (SHOULD BE ZERO SINCE WE'LL ONLY COME
                                            ;THROUGH THIS ONCE), GO HANG THE VOICE UP.
               SWAP    D1                   ;M.S.PRODUCT IS THE PART WE WANT -
;04DEC;
               ADD     #0510H,D1            ;BUMP UP PER VCA FLOOR LEVEL.
;04DEC;
               MOVE    D1,2(A2)             ;PUT IT OUT TO VCA S/H.
;
               SUBQ    #1,V_A_ATT_TIME(A1)  ;UPDATE REMAINING ATTACK PHASE TIME - USED FOR DYNAMIC-
                                            ;ASSIGN VOICE-STEAL TEST, TO FIND TIME-TO-COMPLETION
                                            ;FOR THIS VOICE (IF IT IS NOT STOLEN).
                                            ;NOTE: VALID VALUES ARE IN THE RANGE 0-16383 (130 SEC),
                                            ;TIME-REMAINING COMPUTATION INGORES NEGATIVE VALUES.
;
                                            ;COMPUTE NEXT ENVELOPE POINT:
               LEA     V_A_ATT_RATE(A1),A3  ;FETCH ATTACK RATE (TWO WORDS LONG) -
               MOVEM   (A3)+,D1-D2
               MOVE    D1,D3                ;WE'LL NEED AN EXTRA COPY OF M.S. RATE WORD.
               MULU    D0,D3                ;L.S.LEVEL * M.S.RATE - INTEGER/FRACTION PARTIAL PROD.
               SWAP    D0                   ;SWAP M.S.LEVEL INTO MULTIPLY POSITION.
               MULU    D0,D1                ;M.S.LEVEL * M.S. RATE - LONG INTEGER PARTIAL PRODUCT.
               MULU    D0,D2                ;M.S.LEVEL * L.S.RATE - ANOTHER INTEGER/FRACTION.
                                            ;(WE DON'T BOTHER WITH L.S.LEVEL * L.S.RATE).
               SWAP    D1                   ;LONG INTEGER PARTIAL PRODUCT SHOULD BE <0FFF0H,
               TST     D1                   ;THE (FORMER) M.S.WORD SHOULD BE CLEAR -
               BNE.S   VCA_R_ATT_20         ;ELSE, WE O-FLOED AND ATTACK PHASE IS O-VER.
               ADD.L   D2,D1                ;COMBINE INTEGER/FRACTION AND INTEGER PARTIAL PRODUCTS,
               BCS.S   VCA_R_ATT_20         ;BRANCH IF CARRY - AGAIN, MEANS ATTACK IS OVER.
               ADD.L   D3,D1                ;TOSS IN THE OTHER INTEGER/FRACTION PARTIAL PRODUCT.
               BCS.S   VCA_R_ATT_20         ;BRANCH IF CARRY - AGAIN, MEANS ATTACK IS OVER.
;
;
;04DEC               MOVE.L  D1,V_CUR_VCA(A1)     ;SAVE IT FOR NEXT TIME (ASSUMING THERE IS A NEXT TIME).
;04DEC               CMP.L   #0FFF00000H,D1       ;EH? - HAVE WE PEAKED, DESPITE NO OVERFLOW OR CARRY?
;04DEC               BCS.S   VCA_R_ATT_40         ;BRANCH IF NOT, WE'RE STILL ATTACKING.
;04DECVCA_R_ATT_20
;04DEC               MOVE.W  #VCA_SUSTAIN,V_VCA_VECTOR(A1)  ;ELSE - POUF! WE'RE IN SUSTAIN PHASE.
;04DEC               MOVE.L  #0FFF00000H,V_CUR_VCA(A1)      ;LEAVE THIS IN PLACE FOR RELEASE VECTOR,
;04DEC                                                      ;WHICH KNOWS NOT WHAT WENT ON BEFORE.
;
;
               MOVE.L  D1,V_CUR_VCA(A1)     ;SAVE IT FOR NEXT TIME (ASSUMING THERE IS A NEXT TIME).
               CMP.L   #0FAE00000H,D1       ;EH? - HAVE WE PEAKED, DESPITE NO OVERFLOW OR CARRY?
               BCS.S   VCA_R_ATT_40         ;BRANCH IF NOT, WE'RE STILL ATTACKING.
VCA_R_ATT_20
               MOVE.W  #VCA_SUSTAIN,V_VCA_VECTOR(A1)  ;ELSE - POUF! WE'RE IN SUSTAIN PHASE.
               MOVE.L  #0FAE00000H,V_CUR_VCA(A1)      ;LEAVE THIS IN PLACE FOR RELEASE VECTOR,
                                                      ;WHICH KNOWS NOT WHAT WENT ON BEFORE.
;
;
               CLR     V_A_ATT_TIME(A1)     ;MAKE SURE NO ATTACK TIME APPEARS TO BE LINGERING ON.
VCA_R_ATT_40
               MOVE    V_N_OFF_TIME(A1),D0  ;IS A DELAYED SELF-GATE-OFF TIMING OUT?
               BEQ.S   VCA_R_ATT_EXIT       ;BRANCH IF NOT - EXIT.
               CMP     REAL_TIME,D0         ;IF SO, DID WE PASS GATE-OFF TIME YET?
               BCC.S   VCA_R_ATT_EXIT       ;BRANCH IF NOT, ELSE ....
VCA_R_ATT_60
               CLR     V_N_OFF_TIME(A1)     ;VOICE GATING ITSELF OFF - STOP THE TIMER,
               MOVE    V_THIS_VOICE(A0),D0  ;FETCH INDEX OF THIS VOICE,
               BSET    D0,VOICES_TO_OFF     ;POST A REQUEST TO GATE THIS VOICE OFF FROM BACKGROUND.
VCA_R_ATT_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCA_RELEASE
               MOVE.L  V_CUR_VCA(A1),D0     ;CURRENT ENVELOPE POINT BEFORE LEVEL SCALING.
               MOVE.L  D0,D1                ;COPY FOR LEVEL-SCALE, OUTPUT.
               SWAP    D1                   ;FOR LEVEL-SCALE/OUTPUT WE ONLY USE M.S.WORD.
               MULU    V_A_SUS_LEVEL(A1),D1 ;SCALE BY COMBINED LEVEL FACTOR.
               BEQ     VCA_REL_EXIT         ;IF RESULT = 0, THEN V_A_SUS_LEVEL = 0 - DON'T BOTHER
                                            ;WITH VECTOR ACTION AT ALL (VCA SHOULD BE AT ZERO SINCE
                                            ;WE PRESUMABLY CAUGHT SAME CONDITION IN ONE OF THE
                                            ;ATTACK ROUTINES, WHICH ALSO PRESUMABLY POSTED A VOICE
                                            ;GATE-OFF REQUEST - WE'RE JUST MARKING TIME NOW ....)
;
               SWAP    D1                   ;M.S.PRODUCT IS THE PART WE WANT -
;04DEC;
               ADD     #0510H,D1            ;BUMP UP PER VCA FLOOR LEVEL.
;04DEC;
               MOVE    D1,2(A2)             ;PUT IT OUT TO VCA S/H.
;
               SUBQ    #1,V_A_REL_TIME(A1)  ;UPDATE REMAINING RELEASE PHASE TIME - USED BY DYNAMIC-
                                            ;ASSIGN VOICE-STEAL TEST, TO FIND TIME-TO-COMPLETION
                                            ;FOR THIS VOICE (IF IT IS NOT STOLEN).
                                            ;NOTE: VALID VALUES ARE IN THE RANGE 0-16383 (130 SEC),
                                            ;TIME-REMAINING COMPUTATION INGORES NEGATIVE VALUES.
;
                                            ;COMPUTE NEXT ENVELOPE POINT:
               LEA     V_A_REL_RATE(A1),A3  ;ACCESS RELEASE RATE (TWO WORDS LONG) -
               MOVEM   (A3)+,D1-D2          ;D1-D2 GET M.S./L.S. RATE WORDS RESPECTIVELY -
               MOVE    D1,D3                ;WE'LL NEED AN EXTRA COPY OF M.S. RATE WORD.
               MULU    D0,D3                ;L.S.LEVEL * M.S.RATE.
               SWAP    D0                   ;SWAP M.S.LEVEL INTO MULTIPLY POSITION.
               MULU    D0,D1                ;M.S.LEVEL * M.S. RATE.
               MULU    D0,D2                ;M.S.LEVEL * L.S.RATE.
                                            ;(WE DON'T BOTHER WITH L.S.LEVEL * L.S.RATE).
               CLR     D2                   ;YET MORE KLUDGERY - DROP L.S.WORDS OF BOTH LOW-ORDER
               SWAP    D2                   ;PRODUCTS, THEN ADD THEM TO M.S.PRODUCT - NO ROUNDING.
               CLR     D3
               SWAP    D3
               ADD.L   D2,D1                ;COMBINE THE PARTIAL PRODUCTS WITH M.S.PRODUCT.
               ADD.L   D3,D1                ;SO, THIS BE IT -
               MOVE.L  D1,V_CUR_VCA(A1)     ;SAVE IT FOR NEXT TIME (ASSUMING THERE IS A NEXT TIME).
               SWAP    D1                   ;NOW, BACK INTO 12-BIT LAND TO SEEK ENVELOPE DEATH:
;04DEC               CMP     #300H,D1             ;HAS VCA LEVEL DROPPED OUT OF SIGHT YET?
;04DEC;
               CMP     #010H,D1             ;HAS VCA LEVEL "HIT THE FLOOR" YET?
;04DEC;
               BCC.S   VCA_REL_80           ;BRANCH IF NOT - WE'RE STILL RELEASING.
;
               MOVE.W  #VCA_OFF,V_VCA_VECTOR(A1) ;ELSE - ZIPP! WE'RE AT THE END OF THE LINE -
;05NOV               MOVE.W  #VCF_OFF,V_VCF_VECTOR(A1) ;WASTE NO FURTHER TIME ON VCF OR PITCH ENVELOPES.
               MOVE.W  #VCF_MAINTAIN,V_VCF_VECTOR(A1) ;(NO NEED - UNDESIRABLE, EVEN - TO DROP VCF
                                                      ;ALL THE WAY TO THE FLOOR - JUST DROP IT TO
                                                      ;ITS FINAL ENVELOPE VALUE.
               MOVE.W  #NOTHINGNESS,V_BEND_VECTOR(A1)
               TST     V_PRIO_TIME(A0)      ;HAS SOMEONE ALREADY GOT PLANS FOR THIS VOICE?
               BNE.S   VCA_REL_EXIT         ;BRANCH IF YES - LEAVE ALLOCATION, ETC STATUS: "QUO."
                                            ;ELSE, WE GOTS SOME TIDYING UP TO DO:
               CLR     V_IDENTITY(A0)       ;LET NO ONE WASTE TIME TRYING TO GATE THIS VOICE OFF.
               MOVE    #8000H,V_PITCH_PAN(A1)    ;JAM PAN VALUE TO CENTER.
               MOVE    V_THIS_VOICE(A0),D0  ;FETCH VOICE NUMBER FOR THIS VOICE,
               BSET    D0,CHIPS_TO_ZERO     ;REQUEST THIS VOICE'S CHIP FORCED TO ZERO-LOCATION -
                                            ;THIS GETS PICKED UP IN BACKGROUND ROTATION.
               BCLR    D0,VOICES_TO_REL     ;IF RELEASE REQUEST WAS PENDING - FORGET IT.
               BCLR    D0,VOICES_TO_OFF     ;LIKEWISE FOR ANY PENDING GATE-OFF REQUEST.
VCA_REL_40
               BCLR    D0,ACTIVE_DYNAMIC    ;CLEAR DYN-ASSIGN ACTIVE STATUS BIT FOR THIS VOICE -
               BEQ.S   VCA_REL_60           ;IF IT WAS CLEAR, WE GOT A FIXED-ASSIGN VOICE ANYWAY.
               BSET    D0,IDLE_DYNAMIC      ;ELSE, STICK IT BACK IN THE IDLE DYNAMIC-ASSIGN GROUP,
               BCLR    D0,CUR_SQ_DYN_ON     ;REMOVE IT FROM MAP OF DYN-ASSIGN GATED-ON SEQ VOICES.
               BCLR    D0,PRIO_DYNAMIC      ;VOICE WAS PROBABLY NOT STILL IN PRIORITY SEQUENCE,
                                            ;BUT WHAT THE HELL - LET'S BULLETPROOF A BIT.
               BRA.S   VCA_REL_EXIT
VCA_REL_60
               BCLR    D0,CUR_SQ_FIX_ON     ;FIXED-ASSIGN VOICE - REMOVE IT FROM MAP OF FIXED-
               BRA.S   VCA_REL_EXIT         ;ASSIGN GATED-ON SEQ VOICES - EVEN IF IT WASN'T THERE.
;
VCA_REL_80
               MOVE    V_N_OFF_TIME(A1),D0  ;IS A DELAYED SELF-GATE-OFF TIMING OUT?
               BEQ.S   VCA_REL_EXIT         ;BRANCH IF NOT - EXIT.
               CMP     REAL_TIME,D0         ;IF SO, DID WE PASS GATE-OFF TIME YET?
               BCC.S   VCA_REL_EXIT         ;BRANCH IF NOT,
               CLR     V_N_OFF_TIME(A1)     ;ELSE STOP THE TIMER,
               MOVE    V_THIS_VOICE(A0),D0  ;FETCH INDEX OF THIS VOICE,
               BSET    D0,VOICES_TO_OFF     ;POST A REQUEST TO GATE THIS VOICE OFF FROM BACKGROUND.
VCA_REL_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCA_SUSTAIN
               MOVE    V_A_SUS_LEVEL(A1),D1 ;LEVEL SCALING FACTOR IS ALSO SUSTAIN LEVEL,
                                            ;HOW INCREDIBLY CONVENIENT.
               BEQ.S   VCA_SUS_EXIT         ;IF RESULT = 0, THEN V_A_SUS_LEVEL = 0 - DON'T BOTHER
                                            ;WITH VECTOR ACTION AT ALL (VCA SHOULD BE AT ZERO SINCE
                                            ;WE PRESUMABLY CAUGHT SAME CONDITION IN ONE OF THE
                                            ;ATTACK ROUTINES, WHICH ALSO PRESUMABLY POSTED A VOICE
                                            ;GATE-OFF REQUEST - WE'RE JUST MARKING TIME NOW ....)
;
;04DEC;
               MULU    #0FAE0H,D1           ;SCALE IT BY PEAK RAW VCA ENVELOPE VALUE
                                            ;(ADJUSTED FOR VCA FLOOR LEVEL),
               SWAP    D1                   ;AHH - GOTTA SWAP, DAD.
               ADD     #0510H,D1            ;BUMP UP PER VCA FLOOR LEVEL.
;04DEC;
               MOVE    D1,2(A2)             ;OUT SHE GO.
;
               MOVE    V_A_SUS_TIME(A1),D0  ;HAVE A LOOK AT SUSTAIN TIME -
               BMI.S   VCA_SUS_40           ;IF NEGATIVE, WE STAY HERE UNTIL NOTE-OFF WALKS IN
                                            ;AND DROPS RELEASE VECTOR IN OUR LAP.
               DBRA    D0,VCA_SUS_20        ;ELSE WE'RE TIMING OUT - BRANCH IF NOT DONE.
               MOVE.W  #VCA_RELEASE,V_VCA_VECTOR(A1)  ;TIMED OUT - PUMM! WE'RE IN RELEASE PHASE.
               BSR     STOP_SUSTAIN_LOOP              ;THUS, GET OUT OF SUSTAIN LOOP, IF IN ONE.
               CLR     V_A_SUS_TIME(A1)     ;MAKE SURE NO SUSTAIN TIME APPEARS TO BE LINGERING ON.
               BRA.S   VCA_SUS_40
VCA_SUS_20
               MOVE    D0,V_A_SUS_TIME(A1)  ;UPDATE REMAINING GATE TIME VALUE.
VCA_SUS_40
               MOVE    V_N_OFF_TIME(A1),D0  ;IS A DELAYED SELF-GATE-OFF TIMING OUT?
               BEQ.S   VCA_SUS_EXIT         ;BRANCH IF NOT - EXIT.
               CMP     REAL_TIME,D0         ;IF SO, DID WE PASS GATE-OFF TIME YET?
               BCC.S   VCA_SUS_EXIT         ;BRANCH IF NOT,
               CLR     V_N_OFF_TIME(A1)     ;ELSE STOP THE TIMER,
               MOVE    V_THIS_VOICE(A0),D0  ;FETCH INDEX OF THIS VOICE,
               BSET    D0,VOICES_TO_OFF     ;POST A REQUEST TO GATE THIS VOICE OFF FROM BACKGROUND.
VCA_SUS_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCA_OFF
               MOVE    #0,2(A2)             ;NOT MUCH TO THIS ONE.
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCF_ATTACK
               MOVE    V_CUR_VCF(A1),D0     ;FETCH CURRENT VALUE FOR ENVELOPED PORTION OF VCF.
               MOVE    D0,D1                ;COPY FOR SCALING, OUTPUT.
               MULU    V_F_SUS_LEVEL(A1),D1 ;SCALE BY COMBINED SCALE FACTOR.
               SWAP    D1                   ;USE ONLY M.S.WORD.
               ADD     V_F_CUTOFF(A1),D1    ;COMBINE WITH (STATIC) CUTOFF VALUE.
               BCC.S   VCF_ATT_20           ;BRANCH IF NO OVERFLOW,
               MOVEQ   #NEG_EXT+0F0H,D1     ;ELSE OUTPUT LIMIT VALUE ( = 0FFFH, 12 BITS).
VCF_ATT_20
               MOVE    D1,(A2)              ;OUTPUT NEW VCF CONTROL VALUE.
;
               ADD     V_F_ATT_RATE(A1),D0  ;COMPUTE NEW VCF ENVELOPE POINT -
               BCC.S   VCF_ATT_40           ;BRANCH IF NO OVERFLOW - WE STILL BE IN ATTACK.
               MOVE.W  #VCF_SUSTAIN,V_VCF_VECTOR(A1)  ;ELSE - SET UP SUSTAIN VECTOR,
               MOVEQ   #NEG_EXT+0F0H,D0               ;LEAVE THIS VALUE FOR USE BY VCF_RELEASE.
VCF_ATT_40
               MOVE    D0,V_CUR_VCF(A1)     ;SAVE NEW VCF ENVELOPE POINT.
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCF_RELEASE
               MOVE    V_CUR_VCF(A1),D0     ;FETCH CURRENT VALUE FOR ENVELOPED PORTION OF VCF.
               MOVE    D0,D1                ;COPY FOR SCALING, OUTPUT.
               MULU    V_F_SUS_LEVEL(A1),D1 ;SCALE BY COMBINED SCALE FACTOR.
               SWAP    D1                   ;USE ONLY M.S.WORD.
               ADD     V_F_CUTOFF(A1),D1    ;COMBINE WITH (STATIC) CUTOFF VALUE.
               BCC.S   VCF_REL_20           ;BRANCH IF NO OVERFLOW,
               MOVEQ   #NEG_EXT+0F0H,D1     ;ELSE OUTPUT LIMIT VALUE ( = 0FFFH, 12 BITS).
VCF_REL_20
               MOVE    D1,(A2)              ;OUTPUT NEW VCF CONTROL VALUE.
;
               SUB     V_F_REL_RATE(A1),D0  ;COMPUTE NEW VCF ENVELOPE POINT -
               BCS.S   VCF_REL_30           ;IF WE UNDERFLO'D, WELL, THAT IS THAT.
               CMP     #300H,D0             ;ENVELOPE TERMINATES AT 030H (0300H TO CONTROL-DAC) -
               BCC.S   VCF_REL_40           ;BRANCH IF NOT THERE YET - WE STILL BE IN RELEASE.
VCF_REL_30
               MOVE.W  #VCF_MAINTAIN,V_VCF_VECTOR(A1) ;GONE - SET UP POST-RELEASE VECTOR, EXIT.
               BRA.S   VCF_REL_EXIT
VCF_REL_40
               MOVE    D0,V_CUR_VCF(A1)     ;SAVE NEW VCF ENVELOPE POINT.
VCF_REL_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCF_SUSTAIN
               MOVE    V_F_SUS_LEVEL(A1),D0 ;YES - SUS LEVEL = COMBINED LEVEL SCALE FACTOR.
               ADD     V_F_CUTOFF(A1),D0    ;BUT, DON'T LET'S FORGET STATIC CUTOFF LEVEL ....
               BCC.S   VCF_SUS_20           ;BRANCH AHEAD IF NO OVERFLOW OCCURRED,
               MOVEQ   #NEG_EXT+0F0H,D0     ;ELSE OUTPUT LIMIT VCF VALUE ( = 0FFFH, 12 BITS).
VCF_SUS_20
               MOVE    D0,(A2)              ;OUT TO THE VCF S/H IT GOES - BYE.
;
               MOVE    V_F_SUS_TIME(A1),D0  ;HAVE A LOOK AT SUSTAIN TIME -
               BMI.S   VCF_SUS_EXIT         ;IF NEGATIVE, WE STAY HERE UNTIL NOTE-OFF WALKS IN
                                            ;AND DROPS RELEASE VECTOR IN OUR LAP.
               DBRA    D0,VCF_SUS_40        ;ELSE WE'RE TIMING OUT - BRANCH IF NOT DONE.
               MOVE.W  #VCF_RELEASE,V_VCF_VECTOR(A1)  ;TIMED OUT - PUMM! WE'RE IN RELEASE PHASE.
               BRA.S   VCF_SUS_EXIT
VCF_SUS_40
               MOVE    D0,V_F_SUS_TIME(A1)  ;SO LONG UNTIL NEXT .... TIME.
VCF_SUS_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCF_MAINTAIN
               MOVE    V_F_CUTOFF(A1),(A2)  ;OUTPUT CUTOFF WITHOUT ANY ENVELOPE COMPONENT.
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
VCF_OFF
               MOVE    #0,(A2)              ;NOT MUCH TO THIS ONE.
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
               SKIP
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
; PITCH BEND SUSTAIN VECTOR -
; NOTHING DOING, BUT DISTINCT FROM ACTUAL NOTHINGNESS SO THAT GATE-OFF
; CAN CAUSE TRANSITION TO BEND_RELEASE IF BEND IS UNDER GATE CONTROL.
;
BEND_SUSTAIN
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
BEND_RELEASE
               BSR.S   PITCH_OUTPUT         ;OUTPUT CURRENT PITCH VALUE TO CHIP.
;
               TST     D0                   ;IS RAW PITCH ENVELOPE VALUE = 0?
               BNE.S   BEND_REL_20          ;BRANCH IF NOT,
               MOVE.W  #NOTHINGNESS,V_BEND_VECTOR(A1) ;ELSE, HANG UP THE BEND VECTOR.
               BRA.S   BEND_REL_EXIT
BEND_REL_20
               MULU    V_BEND_RATE(A1),D0   ;COMPUTE NEXT ENVELOPE POINT:
               SWAP    D0                   ;RESULT IN M.S.WORD - SINGLE PRECISION, NO ROUNDING.
               CMP     #64H,D0              ;HAS BEND ENVELOPE DROPPED OUT OF SIGHT YET?
               BCC.S   BEND_REL_40          ;BRANCH IF NOT - WE'RE STILL RELEASING.
               CLR     D0                   ;ELSE, LEAVE SIGNAL TO DISCONNECT OURSELF NEXT TIME -
                                            ;THAT IS, AFTER PUTTING OUT UN-BENT FINAL PITCH.
BEND_REL_40
               MOVE    D0,V_CUR_PITCH(A1)   ;SAVE NEW PITCH ENVELOPE POINT FOR NEXT TIME.
BEND_REL_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
;
;
;
;VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
;
; PITCH OUTPUT ROUTINE - INCORPORATES PITCH-BEND PARAMETERS.
; USED BY PITCH BEND VECTOR FOR OUTPUTTING CURRENT PITCH VALUE TO CHIP.
; ALSO USED BY START_THIS_CHIP:PRIORITY, TO OUTPUT INITIAL VALUE -
; THIS HAPPENS EVEN WHEN BEND AMOUNT AND RATE ARE SET TO 0.
; REGISTER CONVENTIONS ARE SAME AS FOR NON_PRIO ROUTINES IN GENERAL,
; PLUS RETURNS WITH RAW PITCH ENVELOPE VALUE (V_CUR_PITCH) IN D0,
; FOR USE BY BEND_RELEASE IN COMPUTING NEXT POINT.
; NOTE: ALSO REQUIRES A VALID VALUE IN V_CHIP_MISC SO AS NOT TO TRASH CHIP.
;
PITCH_OUTPUT
               MOVE    V_CUR_PITCH(A1),D0   ;CURRENT ENVELOPE POINT BEFORE LEVEL SCALING.
               MOVE    D0,D1                ;COPY FOR LEVEL-SCALE, OUTPUT.
               MOVE    V_BEND_LEVEL(A1),D2  ;FETCH COMBINED LEVEL FACTOR - IT'S BIPOLAR ....
               BMI.S   PIT_OUT_10           ;BRANCH IF NEGATIVE.
               MULU    D2,D1                ;SCALE BY COMBINED LEVEL FACTOR, POSITIVE STYLE ....
               NEG.L   D1                   ;TURN IT AROUND GOING THE "RIGHT" WAY.
               BRA.S   PIT_OUT_20
PIT_OUT_10
               NEG     D2                   ;MAKE NEGATIVE COMBINED LEVEL FACTOR POSITIVE,
               MULU    D2,D1                ;DO THE SCALING, AND DON'T TURN IT AROUND.
PIT_OUT_20
               SWAP    D1                   ;M.S.PRODUCT IS PART WE WANT - IT'S TUNE TABLE OFFSET.
               BCLR    #0,D1                ;AND IT BETTER BE EVEN, OR  ----- Address Error -----
               ADD     V_FIN_PITCH(A1),D1   ;COMBINE OFFSET INDEX WITH FINAL PITCH INDEX VALUE,
               CMP     #MIN_TUNE_INDEX,D1   ;RUNNET THU LIMITZ CHEK.
               BGE.S   PIT_OUT_30
               MOVE    #MIN_TUNE_INDEX,D1
               BRA.S   PIT_OUT_40
PIT_OUT_30
               CMP     #MAX_TUNE_INDEX,D1
               BLE.S   PIT_OUT_40
               MOVE    #MAX_TUNE_INDEX,D1
PIT_OUT_40
               MOVE.L  V_CHIP_ADDR(A0),A3   ;FETCH POINTER TO THIS VOICE'S CHIP.
               MOVE.L  #ROOT_TUNE,A4        ;FETCH POINTER TO "CENTER" OF TUNE TABLE.
               LEA     0(A4,D1),A4          ;POINT TO THE RESULTANT PITCH WORD.
               MOVE.B  V_CHIP_MISC+1(A0),(A3)    ;SELECT CHIP CHANNEL, WITHOUT SMUSHING OTHER BITS.
               MOVE.B  1(A4),PITCHLO(A3)    ;MOVE NEW PITCH VALUE INTO PITCH REGISTER.
               MOVE.B  (A4),PITCHHI(A3)
PIT_OUT_EXIT
               RTS
;
;AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
