;
; MIDI MPU401 Driver for Impulse Tracker.
; Accepts MIDI Input, does MIDI output, but does NOT handle samples at all.
;

        .386P

Segment                 DriverHeader PARA Public 'Code' Use16
                        Assume CS:Driver, DS:Nothing

;***** Driver Header *******

include drhead.inc

EndS

Segment                 Driver PARA Public 'Code' Use16
                        Assume CS:Driver, DS:Nothing

ORG 0
StartDriver:

include vtable.inc

;********  Required ProcedureTable *************

include reqproc.inc

OldIRQHandler           DD      0
MIDIUpdateTimer         DW      0
MIDIUpdateCount         DW      0
MIDIUpdateFlag          DB      0
MIDIDriverMsg           DB      "MPU401 or compatible detected", 13
                        DB      "Address ", 0FDh, "Xh", 0
MIDIReinitMsg           DB      "MPU401 reinitialised", 0

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

Proc            ResetUART               ; Given DX = port.

                ClI

                Inc     DX

                Xor     CX, CX

ResetUART1:
                In      AL, DX
                Test    AL, 40h
                LoopNZ  ResetUART1
                JNZ     ResetUARTFailed

                Mov     AL, 0FFh
                Out     DX, AL

                Xor     CX, CX

ResetUART2:
                In      AL, DX
                Test    AL, 80h
                LoopNZ  ResetUART2
                JNZ     ResetUARTFailed

                Dec     DX
                In      AL, DX
                Inc     DX
                Cmp     AL, 0FEh
                JNE     ResetUART2

                Dec     DX

                DB      85h

ResetUARTFailed:
                StC
                Ret

EndP            ResetUART

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

Proc            SetUARTMode

                Mov     DX, CS:BasePort
                Inc     DX

                Xor     CX, CX

SetUARTMode1:
                In      AL, DX
                Test    AL, 40h
                LoopNZ  SetUARTMode1
                JNZ     SetUARTModeFailed

                Mov     AL, 3Fh
                Out     DX, AL

                Xor     CX, CX

SetUARTMode2:
                In      AL, DX
                Test    AL, 80h
                LoopNZ  SetUARTMode2
                JNZ     SetUARTModeFailed

                Dec     DX
                In      AL, DX
                Inc     DX
                Cmp     AL, 0FEh
                JNE     SetUARTMode2
                Dec     DX

                DB      85h

SetUARTModeFailed:
                StC
                StI
                Ret

EndP            SetUARTMode

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

Proc            UARTOut         ; AL = byte out

                Push    CX
                Push    DX
                Mov     DX, CS:Baseport

                Push    AX
                Xor     CX, CX
                Inc     DX

UARTOut1:
                In      AL, DX
                Test    AL, 40h
                LoopNZ  UARTOut1

                Pop     AX
                JNZ     UARTOutEnd

                Dec     DX
                Out     DX, AL

UARTOutEnd:
                Pop     DX
                Pop     CX

                Ret

EndP            UARTOut

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

InterpretState  DB      0

Proc            SendUARTOut Far ; Local interpreter activated with 0F0h 0F0h.

                Mov     AH, CS:InterpretState
                Cmp     AH, 2
                JB      SendUARTOut1
                JE      SendUARTOutStateInc

                Mov     CS:InterpretState, 0

SendUARTOut4:
                Ret

SendUARTOut1:
                Cmp     AL, 0F0h
                JNE     SendUARTOut2

SendUARTOutStateInc:
                Inc     CS:InterpretState
                Ret

SendUARTOut2:
                Test    AH, AH
                JZ      SendUARTOutEnd

                Push    AX
                Mov     AL, 0F0h
                Call    UARTOut
                Pop     AX
                Mov     CS:InterpretState, 0

SendUARTOutEnd:
                Call    UARTOut
                Ret

EndP            SendUARTOut

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

Proc            MIDIIRQHandler

                PushAD

                Mov     AX, MIDIUpdateTimer
                Add     MIDIUpdateCount, AX
                JC      MIDIIRQHandler1

                Mov     AL, 20h
                Out     20h, AL
                Jmp     MIDIIRQHandler2

MIDIIRQHandler1:
                PushF
                Call    [OldIRQHandler]

MIDIIRQHandler2:
                Xor     MIDIUpdateFlag, 1
                JZ      MIDIIRQHandlerEnd

                Push    DS
                Push    ES

                ClD

                Call    SaveEMSPageFrame
                Call    Update          ; Returns DS:SI, CX

; Have to clear any sample channels playing.
; Kill any active channel

MIDIIRQHandler3:
                Test    Byte Ptr [SI], 1        ; Channel on?
                JZ      MIDIIRQHandler4

                Mov     Word Ptr [SI], 0

                Test    Byte Ptr [SI+3Ah], 80h
                JNZ     MIDIIRQHandler4

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

MIDIIRQHandler4:
                Add     SI, 128
                Dec     CX
                JNZ     MIDIIRQHandler3

                Call    RestoreEMSPageFrame

                Pop     ES
                Pop     DS

MIDIIRQHandlerEnd:
                PopAD
                IRet

EndP            MIDIIRQHandler

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

Proc            SetIRQ

                Mov     AL, 34h
                Out     43h, AL

                Xor     AX, AX
                Mov     ES, AX
                Mov     AX, CS
                ShL     EAX, 16
                Mov     AX, Offset MIDIIRQHandler
                Mov     [ES:20h], EAX

                Ret

EndP            SetIRQ

; ÄÄ DetectCard ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; Returns carry set if error, else carry clear. Has to setup internal vars
; (eg. appropriate IRQ/DMA whatever).
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            DetectCard Far

                Mov     DX, CS:BasePort
                Cmp     DX, 0FFFFh
                JNE     DetectCard1

                Mov     DX, 330h
                Call    ResetUART
                JNC     DetectCard2

                Mov     DX, 300h

DetectCard1:
                Call    ResetUART

DetectCard2:
                Mov     CS:BasePort, DX
                Mov     EAX, 'Jeff'
                Ret

EndP            DetectCard

;ÄÄ InitSound ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; Sets up any memory required for output
; Initiates output
;
; Parameters: AX = Number of Channels
;
; If sucessful, returns:
;   Carry flag clear
;   DS:SI = pointer to text to display
;      AX = parameter 1 in text
;      BX = parameter 2 in text
;      CX = parameter 3 in text
;      DX = parameter 4 in text
;      DI = parameter 5 in text
;
; If unsucessful, returns:
;   Carry flag set
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            InitSound Far

                Push    CS
                Pop     DS
                        Assume DS:Driver

                Xor     AX, AX
                Mov     ES, AX
                Mov     EAX, [ES:20h]
                Mov     OldIRQHandler, EAX
                Call    SetIRQ

                Call    SetUARTMode

                Mov     SI, Offset MIDIDriverMsg
                Mov     AX, BasePort

                ClC
                Ret

EndP            InitSound
                Assume DS:Nothing

;ÄÄ ReInitSound ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; Reinitialises sound output
; Initiates sound output
;
; Parameters: AX = number of channels.
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            ReInitSound Far

                Push    CS
                Pop     DS
                        Assume DS:Driver

                Mov     SI, Offset MIDIReinitMsg
                Mov     BX, 40
                Call    SetInfoLine

                Mov     DX, BasePort
                Call    ResetUART

                Call    SetIRQ
                Call    SetUARTMode

                Ret

EndP            ReInitSound
                Assume DS:Nothing

;ÄÄ UnInitSound ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; Stops sound output, releases any memory used by driver
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            UnInitSound Far

                Mov     DX, CS:BasePort
                Call    ResetUART

                Xor     AX, AX
                Mov     ES, AX
                Mov     EAX, CS:OldIRQHandler
                Mov     [ES:20h], EAX

                Xor     AL, AL
                Out     40h, AL         ; Timer IRQ.
                Out     40h, AL

                Ret

EndP            UnInitSound

;ÄÄ Poll ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; This procedure is called as often as possible by IT.EXE
; AX = Playmode (0 for nothing in particular, 1 = pattern, 2 = song)
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            Poll Far

                ClI

                Call    [UARTBufferEmpty]
                JC      Poll1

PollEnd:
                StI
                Ret

Poll1:
                Mov     DX, BasePort
                Inc     DX

                In      AL, DX
                Test    AL, AL
                JS      PollEnd

                Dec     DX
                In      AL, DX

                Call    [CS:UARTSend]
                Jmp     Poll

EndP            Poll

;ÄÄ SetTempo ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
; Parameters: BX = tempo
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            SetTempo Far

                Push    AX
                Push    BX
                Push    DX

                                        ; Frames per second = 2 * (0.4*Tempo)
                Mov     AX, 0C214h
                Mov     DX, 16h         ; Ticks = (1193181/(2*0.4))/Tempo
                Div     BX

                                        ; AX contains counter.
                Mov     CS:MIDIUpdateTimer, AX

                Out     40h, AL         ; Timer IRQ.
                Mov     AL, AH
                Out     40h, AL

SetTempo2:
                Pop     DX
                Pop     BX
                Pop     AX
                Ret

EndP            SetTempo

;ÄÄ SoundCardScreen ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
;  Function to have driver interactive part of program
;
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ

Proc            SoundCardScreen Far

                Xor     AX, AX
                StC
                Ret

EndP            SoundCardScreen

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

EndDriver:

;********  Provided Variable Table *************

MaxNumberOfChannels     DW      64      ; Maximum number of channels the
                                        ; driver can handle.
StopAfterPlay           DW      0
DefaultChannels         DW      64
DriverFlags             DW      1       ; Handles MIDI Output

                        DW      4 Dup (0)

;********  Provided Procedure Table *************

ProvidedTableStart:

        DW      Offset DetectCard

        DW      Offset InitSound        ; Playing related
        DW      Offset ReinitSound
        DW      Offset UninitSound

        DW      Offset Poll

        DW      Offset SetTempo         ; Sound variable related
        DW      Offset SoundCardScreen  ; SetMixVolume
        DW      Offset SoundCardScreen  ; SetStereo

        DW      Offset SoundCardScreen  ; LoadSample
        DW      Offset SoundCardScreen  ; ReleaseSample
        DW      Offset SoundCardScreen  ; ResetMemory

        DW      Offset SoundCardScreen  ; GetStatus
        DW      Offset SoundCardScreen  ; SoundCardScreen
        DW      Offset SoundCardScreen  ; GetVariable
        DW      Offset SoundCardScreen  ; SetVariable

        DW      Offset SendUARTOut

ProvidedTableEnd:
        DW      32-(ProvidedTableEnd-ProvidedTableStart)/2 Dup (0)

EndS

End
