
Proc            SetGUSRegisters         ; Given DS:SI, CX

                Mov     ES, CS:SongDataArea

                Push    CX
                Push    SI

                Xor     AX, AX

GetOffsetLoop:
                Push    AX
                Push    CX

                Mov     DX, GUSVoiceSelect
                Out     DX, AL                  ; OK.. now to play with this
                                                ; voice.
                Call    GUSDelay

                Inc     DL                      ; DX = select rego

                Mov     BX, [SI]
                Test    BH, 2
                JZ      GetOffsetLoop2

                Mov     BX, 200h
                Mov     [SI], BX

GetOffsetLoop2:
                Test    BL, 1                   ; Channel not on!
                JZ      GetOffsetLoop1

                Test    BH, 1
                JZ      GetOffsetLoop3          ; Prev 1.

                Xor     BX, BX
                Call    SetGUSVolume

                Mov     AL, 80h
                Out     DX, AL
                Add     DL, 2
                In      AL, DX
                Sub     DL, 2
                Mov     AH, AL

                And     AX, 400h
                Out     DX, AL                  ; AL = 0 (Mode control)
                Mov     AL, 3
                Or      AL, AH
                Add     DL, 2
                Out     DX, AL
                Call    GUSDelay
                Out     DX, AL
                Sub     DL, 2

                Jmp     GetOffsetLoop1

GetOffsetLoop3:
                Mov     EAX, [SI+4Ch]
                Mov     [SI+2Ch], EAX

                Mov     AL, 8Ah
                Out     DX, AL

                Inc     DX
                In      AX, DX
                Dec     DX

                And     AH, 31
                ShL     EAX, 16         ; Load high part of EAX

                Mov     AL, 8Bh
                Out     DX, AL
                Inc     DX
                In      AX, DX
                        ; EAX = 000OOOOO OOOOOOOO OOOOOOOF FFFFFFFF
                        ; Need: 00000000 0000OOOO OOOOOOOO OOOOOOOO

                ShR     EAX, 9
                                                ; EAX contains current loc.
                                                ; Convert to Offset (in sample)
                MovZX   EBX, Byte Ptr [SI+36h]
                Sub     EAX, [CS:GUSDataTable+EBX*4]
                Push    CX

                Mov     CL, CS:Compress
                ShL     EAX, CL

                Pop     CX
                Mov     [SI+4Ch], EAX
                Cmp     EAX, [SI+44h]
                JBE     GetOffsetLoop1

                Cmp     Byte Ptr [SI+0Ah], 0
                JNE     GetOffsetLoop1

                Or      Word Ptr [SI], 200h

GetOffsetLoop1:
                Add     SI, SLAVECHANNELSIZE

                Pop     CX
                Pop     AX

                Inc     AX
                Dec     CX
                JNZ     GetOffsetLoop

                Pop     SI
                Pop     CX

;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

                Push    CX
                Push    SI

                Push    SI

                Mov     BX, 1
                Mov     DX, 1

ReinitChannels1:
                Test    Byte Ptr [SI], 1
                JZ      ReinitChannels2

                Mov     BX, DX

ReinitChannels2:
                Inc     DX
                Add     SI, 128
                Loop    ReinitChannels1

                Pop     SI

                Mov     CX, BX

                Cmp     BX, 14
                JAE     RIC3

                Mov     BX, 14

RIC3:
                Cmp     BX, 32
                JB      RIC4

                Mov     BX, 32

RIC4:
                Cmp     BX, CS:UsedChannels
                JE      EndOfFreqChange

                JA      RIC5
                Mov     BX, CS:UsedChannels
                Dec     BX

RIC5:
                                                ; Now.. get new mixspeed.
                                                ; Prepare all new frequencies
                                                ; then write all active voices
                                                ; + all new frequencies.
                                        ; BX = num channels...
                Push    BX
                Push    BX
                Push    BX

                Add     BX, BX          ; BX = pointer into mixtable.
                MovZX   EBX, [CS:GUSMixTable+BX-28]
                Mov     CS:MixSpeed, BX

                Push    CX
                Push    SI

PrepareNewFreq2:
                Test    Byte Ptr [SI], 1
                JZ      PrepareNewFreq3

                Test    Word Ptr [SI], 100h
                JNZ     PrepareNewFreq3

                Mov     EAX, [SI+10h]          ; EAX = freq.
                Push    CX
                Mov     CL, CS:Compress
                ShR     EAX, CL
                Pop     CX
                Xor     EDX, EDX

                Div     EBX                    ; EAX = I portion.

                Push    EAX
                Xor     EAX, EAX
                Div     EBX

                Pop     EDX             ; EDX:EAX = IIII FFFF etc.

                SHRD    EAX, EDX, 22
                Add     AX, 1
                SBB     AX, 0                   ; Just in case!
                And     AX, Not 1
                Mov     [SI+4], AX

PrepareNewFreq3:
                Add     SI, 128
                Loop    PrepareNewFreq2

                Pop     SI
                Pop     CX

                Pop     BX

                Mov     DI, CS:UsedChannels
                Cmp     DI, BX
                JB      KillExtraChannels1

                Mov     DI, BX
                Mov     BX, 32
;                XChg    DI, BX

KillExtraChannels1:
                Cmp     DI, BX
                JAE     KillExtraChannels2

                Mov     DX, GUSVoiceSelect
                Mov     AX, DI
                Out     DX, AL

                Inc     DX              ; Select rego

                Mov     AL, 0Dh         ; Volume Ramp.
                Out     DX, AL

                Mov     AL, 3
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                Xor     AL, AL
                Out     DX, AL                  ; AL = 0 (Mode control)
                Mov     AL, 3
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                Mov     AL, 9
                Out     DX, AL
                Xor     AX, AX
                Inc     DX
                Out     DX, AX
                Dec     DX

                Mov     AL, 0Ch                 ; Set panning.
                Out     DX, AL
                Mov     AL, 8
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                Comment ~

                Call    GUSDelay

                Mov     AL, 0Dh         ; Volume Ramp.
                Out     DX, AL

                Mov     AL, 3
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                Xor     AL, AL
                Out     DX, AL                  ; AL = 0 (Mode control)
                Mov     AL, 3
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                Mov     AL, 9
                Out     DX, AL
                Xor     AX, AX
                Inc     DX
                Out     DX, AX
                Dec     DX

                Mov     AL, 0Ch                 ; Set panning.
                Out     DX, AL
                Mov     AL, 8
                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

                ~

                Inc     DI
                Jmp     KillExtraChannels1

KillExtraChannels2:
                Xor     BX, BX          ; BX = channel number

PrepareNewFreq4:
                Mov     DX, GUSVoiceSelect
                Mov     AL, BL
                Out     DX, AL
                Inc     DX              ; Select rego

                Test    Byte Ptr [SI], 1
                JZ      PrepareNewFreq5

                Test    Word Ptr [SI], 120h     ; New note or new freq?
                JNZ     PrepareNewFreq5

                Or      Byte Ptr [SI], 2h

                Mov     AL, 1
                Out     DX, AL
                Inc     DX
                Mov     AX, [SI+4]
                Out     DX, AX

PrepareNewFreq5:
                Inc     BX
                Add     SI, 128
                Loop    PrepareNewFreq4

                Mov     DX, GUSRegisterSelect
                Mov     AL, 0Eh
                Out     DX, AL
                Add     DL, 2
                Pop     AX
                Dec     AL
                Or      AL, 0C0h
                Out     DX, AL

                Pop     CS:UsedChannels

EndOfFreqChange:

                Pop     SI
                Pop     CX

;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
                Xor     AX, AX

SetGUSRegisters1:
                Push    AX
                Push    CX

                Mov     DX, GUSVoiceSelect
                Out     DX, AL                  ; OK.. now to play with this
                                                ; voice.
                Call    GUSDelay

                Mov     CX, [SI]                ; CX = flags.
                Inc     DL                      ; DX = select rego

SetGUSRegisters21:
                Test    CL, 32                  ; Frequency change
                JZ      SetGUSRegisters7

                Push    DX

                Mov     EAX, [SI+10h]          ; EAX = freq.

                Push    CX
                Mov     CL, CS:Compress
                ShR     EAX, CL
                Pop     CX

                Xor     EDX, EDX
                MovZX   EBX, MixSpeed

                Div     EBX                    ; EAX = I portion.

                Test    EAX, Not 63
                JNZ     SetGUSRegisters6

                Push    EAX

                Xor     EAX, EAX
                Div     EBX

                Pop     EBX             ; EBX:EAX = IIII FFFF etc.

                SHRD    EAX, EBX, 6

                        ; Have:      IIIIIIII IIIIIIII FFFFFFFF FFFFFFFF
                        ; Req:       IIIIIIFF FFFFFFF0

                Pop     DX
                Mov     AL, 1
                Out     DX, AL

                ShR     EAX, 16
                Add     AX, 1
                SBB     AX, 0                   ; Just in case!
                And     AX, Not 1

                Inc     DX
                Out     DX, AX
                Dec     DX

                Jmp     SetGUSRegisters7

SetGUSRegisters6:
                Mov     CH, 2                   ; Signify to turn off channel
                Pop     DX

SetGUSRegisters7:
                MovZX   BX, Byte Ptr [SI+36h]   ; BX = sample number.
                Cmp     BL, 99
                JA      SetGUSRegisters20

                ShL     BX, 2                   ; GUSDataTable+BX = position

                Cmp     DWord Ptr [CS:GUSDataTable+BX], 0FFFFFFFFh
                JNE     SetGUSRegisters10

SetGUSRegisters20:
                Mov     CH, 2
                Jmp     SetGUSRegisters9

SetGUSRegisters10:
                Test    CX, 502h                ; Loop changed?!??!?
                JZ      SetGUSRegisters8

                Mov     AL, 3                   ; Starting location low.
                Out     DX, AL

                Mov     EAX, [SI+40h]
                Call    GUSGetAddress

                                ; Reqd: ...OOOOO OOOOOOOO OOOOOOOI III.....

                Inc     DX
                Out     DX, AX
                Dec     DX

                Mov     AL, 2                   ; Starting location high
                Out     DX, AL

                ShR     EAX, 16
                Inc     DX
                Out     DX, AX
                Dec     DX

                                                ; Ending location...
                Mov     AL, 5                   ; Ending location low
                Out     DX, AL

                Mov     EAX, [SI+44h]
                Call    GUSGetAddress

                Inc     DX
                Out     DX, AX
                Dec     DX

                Mov     AL, 4                   ; Ending location high
                Out     DX, AL

                SHR     EAX, 16
                Inc     DX
                Out     DX, AX
                Dec     DX

SetGUSRegisters8:
                Test    CH, 1                   ; Do Current position?
                JZ      SetGUSRegisters9

                Mov     AL, 0Bh         ; Current location LOW
                Out     DX, AL

                Mov     EAX, [SI+4Ch]
                Mov     [SI+2Ch], EAX

                        ; EAX   = OOOOOOOO OOOOOOOO OOOOOOOO OOOOOOOO
                        ; Req:    xxxOOOOO OOOOOOOO OOOOOOOF FFFFFFFF
                Call    GUSGetAddress

                Mov     DI, AX

                Inc     DX
                Out     DX, AX
                Dec     DX

                Mov     AL, 0Ah         ; Current location HIGH
                Out     DX, AL

                SHR     EAX, 16
                Inc     DX
                Out     DX, AX
                Call    GUSDelay
                Out     DX, AX
                Dec     DX

                Mov     AL, 0Bh
                Out     DX, AL

                Mov     AX, DI
                Inc     DX
                Out     DX, AX
                Dec     DX

SetGUSRegisters9:
                Test    CL, 64                  ; New volume??
                JZ      SetGUSPanning

SetGUSRegisters23:
                Xor     BX, BX
                Test    CH, 8
                JNZ     SetGUSRegistersMuted

                Mov     BL, Byte Ptr [SI+20h]   ; BL = volume, 0->128

SetGUSRegistersMuted:
                Call    SetGUSVolume


SetGUSPanning:
                Test    CH, 128                 ; New panning?
                JZ      SetGUSRegisters5

                Mov     AL, 0Ch                 ; Set panning.
                Out     DX, AL

                Test    CS:Stereo, 1
                JZ      SetGUSRegisters3

                Mov     AL, [SI+37h]
                Cmp     AL, 100
                JNE     SetGUSRegisters4

SetGUSRegisters3:
                Mov     AL, 32                  ; Surround goes to mono :(

SetGUSRegisters4:                               ; AL = 0->64
                ShR     AL, 1   ; AL = 0->32
                Sub     AL, 1
                AdC     AL, 0   ; AL = 0->31
                ShR     AL, 1

                Add     DL, 2
                Out     DX, AL
                Sub     DL, 2

SetGUSRegisters5:
                                                ; Now for control register.
                                                ; If CH | 2, then turn rego OFF
                                                ; If CH | 5, then turn rego ON
                                                ; If CL | 1, then check channel
                Test    CH, 2
                JNZ     SetGUSRegisters11

                Test    CH, 5
                JNZ     SetGUSRegisters12

                Test    CL, 1
                JZ      SetGUSRegisters13

                Mov     AL, 80h                 ; Read voice control
                Out     DX, AL

                Add     DL, 2
                In      AL, DX
                Sub     DL, 2

                Test    AL, 1
                JZ      SetGUSRegisters13

                Xor     BX, BX
                Call    SetGUSVolume

                Jmp     SetGUSRegisters14

SetGUSRegisters11:
                                                ; Turn off.
                Xor     AL, AL
                Out     DX, AL                  ; AL = 0 (Mode control)
                Mov     AL, 2
                Add     DL, 2

                Mov     AH, [SI+18h]            ; 16 bit?
                Add     AH, AH
                Or      AL, AH

                Out     DX, AL
                Call    GUSDelay
                Out     DX, AL
                Sub     DL, 2

                Xor     BX, BX
                Call    SetGUSVolume

SetGUSRegisters14:
                Test    CL, 1
                JZ      SetGUSRegisters13

                Xor     CX, CX                  ; Turn off channel
                Test    Byte Ptr [SI+3Ah], 80h
                JNZ     SetGUSRegisters13

                Mov     BX, [SI+38h]
                And     Byte Ptr [BX], Not 4    ; Signify channel off

                Jmp     SetGUSRegisters13

SetGUSRegisters12:                              ; Turn on
                Xor     AL, AL
                Out     DX, AL

                Mov     AL, [SI+0Ah]

                Cmp     CS:Convert16To8Bit, 0
                JNZ     SetGUSRegistersNo16Bit

                Mov     AH, [SI+18h]            ; 16 bit?
                Add     AH, AH
                Or      AL, AH

SetGUSRegistersNo16Bit:
                Test    CL, 1
                JNZ     SetGUSRegisters15

                Mov     AL, 2
                Xor     CX, CX

SetGUSRegisters15:
                Add     DL, 2
                Out     DX, AL
                Call    GUSDelay
                Out     DX, AL

SetGUSRegisters13:
                And     CX, 0111100010011111b   ; Turns off:
                                                ; 1) Freq, pan & vol recalc
                                                ; 2) New note/note stop/loop cha

                Mov     [SI], CX

                Add     SI, SLAVECHANNELSIZE

                Pop     CX
                Pop     AX

                Inc     AX
                Dec     CX
                JNZ     SetGUSRegisters1

                Ret


EndP            SetGUSRegisters


