; By Joshua Jensen
;
; I realize this isn't the most efficiently coded piece of work, but I was in
; a hurry, so you'll have to deal with it!

ideal
P286
model   Huge
jumps

stack   1024

codeseg

                assume  cs:@code,ds:MyData

Base    dw      220h

proc    UDelay
        push    dx ax
        mov     dx,300h
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        pop     ax dx
        ret
endp    UDelay

; BX:CX Set to whatever.
proc    U_Peek
        push    dx
        mov     dx,[cs:Base]
        add     dx,103h                 ; 103h
        mov     al,43h
        out     dx,al
        inc     dx                      ; 104h
        mov     ax,cx
        out     dx,ax
        dec     dx                      ; 103h
        mov     al,44h
        out     dx,al
        add     dx,2
        mov     al,bl
        out     dx,al
        add     dx,2
        in      al,dx
        pop     dx
        ret
endp    U_Peek

; BX:CX Set to whatever.
; AX Value to poke
proc    U_Poke
        push    dx ax
        mov     dx,[cs:Base]
        add     dx,103h
        mov     al,43h
        out     dx,al
        inc     dx
        mov     ax,cx
        out     dx,ax
        dec     dx
        mov     al,44h
        out     dx,al
        add     dx,2
        mov     al,bl
        out     dx,al
        add     dx,2
        pop     ax
        out     dx,al
        pop     dx
        ret
endp    U_Poke


; DX - Base.
proc    U_Probe
        mov     dx,[cs:Base]
        add     dx,0103h                ; 103h
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,0
        out     dx,al
        call    UDelay
        call    UDelay
        sub     dx,2                    ; 103h
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,1
        out     dx,al
        mov     ax,0AAh
        mov     bx,0
        mov     cx,0
        call    U_Poke
        mov     ax,055h
        mov     bx,1
        call    U_Poke
        mov     bx,0
        call    U_Peek
        push    ax
        mov     ax,0
        call    U_Poke
        sub     dx,2                    ; 103h
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,0
        out     dx,al
        pop     ax
        cmp     al,0AAh
        jnz     @@Nope
        clc
        ret
@@Nope: stc
        ret
endp    U_Probe

proc    U_Reset
        mov     dx,[cs:Base]
        add     dx,103h                 ; 103h
        mov     al,4Ch                  ; Turns on memory writes.
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,1
        out     dx,al
        call    UDelay
        sub     dx,2
        mov     al,4Ch                  ; ???  The samples won't play without it.
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,7
        out     dx,al
        sub     dx,2
        mov     al,0Eh                  ; Turn off stereo (or something).
        out     dx,al                   ; Try taking it out and running PlayFile.
        add     dx,2
        mov     al,0dfh
        out     dx,al

        mov     cx,0                    ; Voice Number
@@VoiceLoop:
        mov     dx,[cs:Base]
        add     dx,102h                 ; 102h
        mov     al,cl
        out     dx,al
        inc     dx                      ; 103h
        mov     al,0
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,3
        out     dx,al
        sub     dx,2                    ; 103h
        inc     cx
        cmp     cx,32
        jl      @@VoiceLoop
        ret
endp    U_Reset


; Returns:
;   0 = No Ram
;   1 = 256k
;   2 = 512k
;   3 = 768k
;   4 = 1024k
proc    U_SizeDRAM
        mov     bx,0
        mov     cx,0
@@DRAMLoop:
        push    cx
        mov     cx,0
        mov     ax,0AAh
        call    U_Poke
        call    U_Peek
        pop     cx
        cmp     al,0AAh
        jnz     @@Exit
        add     bx,4
        inc     cx
        cmp     cx,4
        jbe     @@DRAMLoop
@@Exit: mov     ax,cx
        ret
endp    U_SizeDRAM

; BX - Voice
; AX - Frequency
proc    U_SetFreq               ; Close enough for me...
        xor     dx,dx
        mov     cx,19
        div     cx
        push    ax
        mov     dx,[cs:Base]
        add     dx,102h         ; 102h
        mov     ax,bx
        out     dx,al
        inc     dx              ; 103h
        mov     al,1
        out     dx,al
        inc     dx              ; 104h
        pop     ax
        out     dx,ax
        ret
endp    U_SetFreq


; AX - Voice
; BX - Balance (0-f) 7 is middle
proc    U_SetBalance
        mov     dx,[cs:Base]
        add     dx,102h
        out     dx,al
        inc     dx
        mov     al,0ch
        out     dx,al
        add     dx,2
        mov     ax,bx
        out     dx,al
        ret
endp    U_SetBalance

; AX - Voice
; BX - Volume
proc    U_SetVolume
        mov     dx,[cs:Base]
        add     dx,102h
        out     dx,al
        inc     dx
        mov     al,9
        out     dx,al
        inc     dx
        mov     ax,bx
        out     dx,ax
        ret
endp    U_SetVolume

; AX - Voice
; BX - Mode
;          bit 0: 0=voice on (along with bit 1)
;          bit 1: 0=voice on (along with bit 0)
;          bit 2: 0=8 bit data, 1=16 bit data
;          bit 3: 0=No loop, 1=Loop
;          bit 4: 0=Go forward, 1=Go backward
proc    U_SetLoopMode
        mov     dx,[cs:Base]
        add     dx,102h
        out     dx,al
        inc     dx
        mov     al,80h
        add     dx,2
        in      al,dx
        mov     bh,al
        sub     dx,2
        xor     al,al
        out     dx,al
        add     dx,2
        and     bh,0e7h
        or      bh,bl
        mov     al,bh
        out     dx,al
        ret
endp    U_SetLoopMode

; AX - Voice
proc    U_StopVoice
        mov     dx,[cs:Base]
        add     dx,102h
        out     dx,al
        inc     dx
        mov     al,80h
        add     dx,2
        in      al,dx
        mov     bh,al
        sub     dx,2
        xor     al,al
        out     dx,al
        add     dx,2
        mov     al,bh
        and     bh,0dfh
        or      al,3
        out     dx,al
        call    UDelay
        sub     dx,2
        xor     al,al
        out     dx,al
        add     dx,2
        mov     al,bh
        and     bh,0dfh
        or      al,3
        out     dx,al
        ret
endp    U_StopVoice



SampleBegin     dd      0       ; (Start location in DRAM, apparently 32-byte aligned)
SampleStart     dd      0       ; (Start location of loop)
SampleEnd       dd      0       ; (End of sample)

; AX - Voice
; BX - Mode
proc    U_StartVoice
        push    bx
        mov     dx,[cs:Base]
        add     dx,102h         ; 102h
        out     dx,al

        ; Send sample begin
        inc     dx              ; 103h
        mov     al,0ah
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleBegin]
        mov     cx,[Word cs:SampleBegin+2]
        call    RShift
        out     dx,ax
        dec     dx              ; 103h
        mov     al,0bh
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleBegin]
        shl     ax,9
        out     dx,ax
        dec     dx              ; 103h

        ; Send sample start
        mov     al,2
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleStart]
        mov     cx,[Word cs:SampleStart+2]
        call    RShift
        out     dx,ax
        dec     dx              ; 103h
        mov     al,3
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleStart]
        shl     ax,9
        out     dx,ax
        dec     dx              ; 103h

        ; Send sample end
        mov     al,4
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleEnd]
        mov     cx,[Word cs:SampleEnd+2]
        call    RShift
        out     dx,ax
        dec     dx              ; 103h
        mov     al,5
        out     dx,al
        inc     dx              ; 104h
        mov     ax,[Word cs:SampleEnd]
        shl     ax,9
        out     dx,ax
        dec     dx              ; 103h

        mov     al,0
        out     dx,al
        pop     ax
        add     dx,2            ; 105h
        out     dx,al
        ret
endp    U_StartVoice


; CX:AX  - Number
proc    RShift
        mov     bx,cx
        shr     ax,7
        shr     cx,7
        shl     bx,9
        or      ax,bx
        ret
endp    RShift

; AX - Voice
; Returns:  DX:AX - Position
proc    U_ReadPos
        mov     dx,[cs:Base]
        add     dx,102h         ; 102h
        out     dx,al
        inc     dx              ; 103h
        mov     al,8ah
        out     dx,al
        inc     dx              ; 104h
        in      ax,dx           ; TEMP0
        mov     cx,ax
        dec     dx              ; 103h
        mov     al,8bh
        out     dx,al
        inc     dx              ; 104h
        in      ax,dx           ; TEMP1
        shl     cx,7
        shr     ax,9
        and     ax,7Fh
        mov     dx,ax
        and     dx,0Fh
        mov     ax,cx
        ret
endp    U_ReadPos

; AX-Mixer control
;  bit 0: 0=linein on, 1=linein off
;  bit 1: 0=output on, 1=output off
;  bit 2: 0=micin off, 1=micin on
proc    U_ChangeInOut
        mov     dx,[cs:Base]
        out     dx,al
        ret
endp    U_ChangeInOut

; The only reason I do it this way is because I haven't figured out the dump
; RAM to DRAM via DMA yet.
;
; Dump sample to Ram
;   ES:BX - Max 64k sample to dump to RAM.
;   SI:DI - DRAM location to dump to.
;   CX    - Max bytes to dump.
;   AH    - Xor value for twos complement.  Should actually be negated, but
;           oh, well.
;
; Approximate time to dump 1 megabyte of samples to DRAM (assuming they were
; all loaded in memory) is 5 seconds.
proc    U_DumpSampleToDRAM
        mov     dx,[cs:Base]
        add     dx,103h
        mov     al,44h          ; Dump upper byte, only do it on carry from now
        out     dx,al           ; on.
        add     dx,2
        push    ax
        mov     ax,si
        out     dx,al
        pop     ax
        sub     dx,2
@@MainLoop:
        mov     al,43h
        out     dx,al
        inc     dx
        push    ax
        mov     ax,di
        out     dx,ax
        pop     ax
        dec     dx
        add     di,1
        jnc     @@DumpByte
        inc     si
        mov     al,44h
        out     dx,al
        add     dx,2
        push    ax
        mov     ax,si
        out     dx,al
        pop     ax
        sub     dx,2
@@DumpByte:
        add     dx,4
        mov     al,[es:bx]
        xor     al,ah
        inc     bx
        out     dx,al
        sub     dx,4
        loop    @@MainLoop
        ret
endp    U_DumpSampleToDRAM

proc    U_DumpDRAMToMemory
        mov     dx,[cs:Base]
        add     dx,103h
        mov     al,44h          ; Dump upper byte, only do it on carry from now
        out     dx,al           ; on.
        add     dx,2
        mov     ax,si
        out     dx,al
        sub     dx,2
@@MainLoop:
        mov     al,43h
        out     dx,al
        inc     dx
        mov     ax,di
        out     dx,ax
        dec     dx
        add     di,1
        jnc     @@DumpByte
        inc     si
        mov     al,44h
        out     dx,al
        add     dx,2
        mov     ax,si
        out     dx,al
        sub     dx,2
@@DumpByte:
        add     dx,4
        in      al,dx
        mov     [es:bx],al
        inc     bx
        sub     dx,4
        loop    @@MainLoop
        ret
endp    U_DumpDRAMToMemory


; Carry set - No GUS
; No carry  - GUS at Base
proc    U_DetectGUS
        mov     [Word cs:Base],210h
@@TestIt:
        call    U_Probe
        jnb     @@FoundIt
        add     [Word cs:Base],10h
        cmp     [Word cs:Base],270h
        jb      @@TestIt
        stc
@@FoundIt:
        ret
endp    U_DetectGUS

proc    LoadDumpSample
        mov     ah,48h
        mov     bx,14848/4+1
        int     21h
        mov     [SampleSeg],ax

        mov     ax,3D00h
        mov     dx,offset FName
        int     21h
        mov     bx,ax
        push    ds
        mov     ah,3Fh
        mov     cx,14848
        mov     dx,0
        mov     ds,[SampleSeg]
        int     21h
        pop     ds

        mov     ax,3E00h
        int     21h

        mov     es,[SampleSeg]
        mov     bx,0
        mov     cx,14848
        mov     si,0
        mov     di,0
        mov     ah,10000000b
        call    U_DumpSampleToDRAM
        ret
endp    LoadDumpSample

proc    LoadDumpSample2
        mov     ah,48h
        mov     bx,9900/4+1
        int     21h
        mov     [SampleSeg2],ax

        mov     ax,3D00h
        mov     dx,offset FName2
        int     21h
        mov     bx,ax
        push    ds
        mov     ah,3Fh
        mov     cx,9900
        mov     dx,0
        mov     ds,[SampleSeg2]
        int     21h
        pop     ds

        mov     ax,3E00h
        int     21h

        mov     es,[SampleSeg2]
        mov     bx,0
        mov     cx,9900
        mov     si,1
        mov     di,0
        mov     ah,0

        call    U_DumpSampleToDRAM
        ret
endp    LoadDumpSample2

Balance db      7
Volume  dw      0d000h
Mode    db      0

Start:
        mov     ax,es
        mov     bx,zzzzzseg
        sub     bx,ax
        add     bx,2
        mov     ah, 4ah
        int     21h

        mov     ax,MyData
        mov     ds,ax

        call    U_DetectGUS
        call    U_Reset
        mov     al,0
        call    U_ChangeInOut

        call    LoadDumpSample
        call    LoadDumpSample2

        mov     ax,1
        mov     bx,7
        call    U_SetBalance
        mov     bx,1
        mov     ax,8448
        call    U_SetFreq
        mov     ax,1
        mov     bx,0ffffh
        call    U_SetVolume
        mov     [Word cs:SampleBegin],0
        mov     [Word cs:SampleBegin+2],0
        mov     [Word cs:SampleStart],0
        mov     [Word cs:SampleStart+2],0
        mov     [Word cs:SampleEnd],14847
        mov     [Word cs:SampleEnd+2],0
        mov     ax,1
        mov     bx,00001000b
        call    U_StartVoice
        mov     ax,0
        mov     bx,7
        call    U_SetBalance
        mov     bx,0
        mov     ax,27186
        call    U_SetFreq
        mov     ax,0
        mov     bx,[cs:Volume]
        call    U_SetVolume
        mov     [Word cs:SampleBegin],0
        mov     [Word cs:SampleBegin+2],1
        mov     [Word cs:SampleStart],0
        mov     [Word cs:SampleStart+2],1
        mov     [Word cs:SampleEnd],9899
        mov     [Word cs:SampleEnd+2],1
        mov     ax,0
        mov     bx,00001000b
        call    U_StartVoice

@@KeyLoop:
        mov     ah,0
        int     16h
        cmp     al,'='
        jnz     @@CheckNext
        cmp     [Byte cs:Balance],15
        jz      @@KeyLoop
        inc     [Byte cs:Balance]
        mov     ax,1
        mov     bl,[cs:Balance]
        call    U_SetBalance
        jmp     @@KeyLoop
@@CheckNext:
        cmp     al,'-'
        jnz     @@CheckVolUp
        cmp     [Byte cs:Balance],0
        jz      @@KeyLoop
        dec     [Byte cs:Balance]
        mov     ax,1
        mov     bl,[cs:Balance]
        call    U_SetBalance
        jmp     @@KeyLoop
@@CheckVolUp:
        cmp     al,'2'
        jnz     @@CheckVolDown
        add     [Word cs:Volume],500
        jnc     @@SetVol
        mov     [Word cs:Volume],0ffffh
@@SetVol:
        mov     ax,0
        mov     bx,[cs:Volume]
        call    U_SetVolume
        jmp     @@KeyLoop
@@CheckVolDown:
        cmp     al,'1'
        jnz     @@CheckSpace
        sub     [Word cs:Volume],500
        jnc     @@SetVol2
        mov     [Word cs:Volume],0
@@SetVol2:
        mov     ax,0
        mov     bx,[cs:Volume]
        call    U_SetVolume
        jmp     @@KeyLoop
@@CheckSpace:
        cmp     al,' '
        jnz     @@CheckQuit
        xor     [Byte Mode],00000011b
        mov     ax,1
        mov     bl,[Mode]
        call    U_SetLoopMode
        jmp     @@KeyLoop

@@CheckQuit:
        cmp     al,27
        jnz     @@KeyLoop

        mov     ax,0
        call    U_StopVoice
        mov     ax,1
        call    U_StopVoice

        mov     ah,49h
        mov     es,[SampleSeg]
        int     21h
        mov     ah,49h
        mov     es,[SampleSeg2]
        int     21h
        mov     ax,4C00h
        int     21h

segment MyData

SampleSeg       dw      ?
SampleSeg2      dw      ?
FName           db      's',0
FName2          db      'orchhit',0

ends    MyData

segment zzzzzseg
        db      16 dup (?)
ends    zzzzzseg

         end     Start
