;*********************************************************************
;
; DSP.ASM - SB DSP emulation
;
; Contains Assembly routines used for SoundBlaster DSP emulation
; The only entry points include:
;    proc_2xE_read()  - handles read from 2xE
;    proc_2x6_write() - handles write to 2x6
;    proc_2xC_write() - handles write to 2xC
;    reset_2xF()      - resets interrupts associated with 2xF
;   flip_on()        - turns 2xC MSB flipping on
;   flip_off()       - turns 2xC MSB flipping off
;
;*********************************************************************
; THIS MODULE MUST ALL REMAIN RESIDENT
.386

INCLUDE sbosdefs.inc
INCLUDE dac.inc
INCLUDE dsp.inc
INCLUDE gf1hware.inc

_DATA     segment word public use16 'DATA'
_DATA     ends
_BSS      segment word public use16 'BSS'
_BSS      ends
_TEXT     segment byte public use16 'CODE'
_TEXT     ends

DGROUP    group    _DATA,_BSS,_TEXT

_DATA     segment word public use16 'DATA'

;---------------------------------------------------------------------
; LIST OF PUBLICS
;---------------------------------------------------------------------
public proc_2xE_read
public proc_2x6_write
public proc_2xC_write
public reset_2xF
public dsp_direct_dac
public _dsp_direct_mode
public change_to_pio
public change_from_pio
public force_sb_irq
public _fake_sb_irq
public simulate_sb_irq
public dsp_status ; for the command 10 in the 2xC handler in ints.asm
public _dsp_dma_rate
public _dsp_old_dma_rate
public _dsp_dma_size
public trd_bit
IFDEF VAR_PCM
public _asm_codec_freq
ENDIF
public decomp_a_buffer
public prog_pcdma_adpcm_ctr
public prog_dma_stuff
public prog_codec_play_ctr
public begin_adpcm
public half_adpcm
public dsp_dac_dma_continue
public dsp_speaker_on
public dsp_adc_dma_continue
public dsp_prog_dac_block_size
public dsp_prog_adc_block_size
public flip_on
public flip_off
public dsp_encoded
public convert_first_seed
public convert_second_seed
public last_buff_cnt
public app_dma_seg
public app_dma_off
public app_dma_count

;---------------------------------------------------------------------
; LIST OF EXTERNS
;---------------------------------------------------------------------
;
extrn reset_SB:near
extrn timer_prog:near
extrn _synth_handler:near
extrn _write_dram:near
;
; Masks
;
extrn _regctl_mask:byte
extrn _fm_data:byte
extrn _mixer_mask:byte
extrn _reg_index:word
extrn _reg_data_high:word
extrn _reg_data_low:word
extrn _shared:word
extrn _dram_temp_buff:dword
;
; External C functions
;
extrn _reset_synth:near
extrn _SetPic:near
extrn _set_dma_rate:near
extrn start_timer:near
extrn stop_timer:near
;
; C variables
;
; All needed ports
;
extrn _reg_mixer:word
extrn _reg_sb2xC:word
extrn _reg_2xC_write:word
extrn _reg_sb2xE:word
extrn _reg_update_sb2xA:word
extrn _reg_update_sb2xE:word
extrn _reg_2xFcontrol:word
extrn _reg_2xBindexed:word
;
extrn _reg_codec_addr:word
extrn _reg_codec_data:word
extrn _reg_codec_status:word
extrn _reg_codec_pio:word
extrn _mixer_mask:byte

; Debugging data space
;
IFDEF D_SB
extrn _add_to_debug_table:near
extrn changeborder:near
extrn _enable_debug:word
public debug_dma
ENDIF
;
; NMI DSP COMMAND JUMP TABLE
;
; This table and data are used by the NMI routine strictly for calling the
; correct DSP data handling routine with the correct number of arguments.
; The NMI routine looks here for the matching DSP command and number of
; arguments.  When the NMI has been called enough for any one command where
; all the arguments are collected, control passes to the address listed in the
; address field for that command and all appropriate variables are reset.
;
; Also note that all of the commands with the high bit set are mapped lower
; to a value without the high bit set. Reads from 2xC flip the bit...
;
dsp_status      dw      0       ; all flags off
dsp_com_offset  db      0
dsp_args_got    db      0
dsp_args_needed db      0
dsp_args        db      0,0,0,0
dsp_first_byte  db      0
dsp_second_byte db      0

trd_bit         db      0
version_count   db      0
_dsp_maj_ver    db      2
_dsp_min_ver    db      0
_dsp_crypt_seed db      40H
_dsp_ack_data   db      0

_dsp_dma_rate    dw      0000H   ; rate in hertz
_dsp_old_dma_rate dw     0ffffH  ; prev rate in hertz
_dsp_dma_size    dw      07ffH   ; dma size in bytes
_dsp_direct_mode db      0       ; 1 if in direct I/O mode
old_time_const   db      0       ; OLD time const. Used for high speed xfer

begin_adpcm EQU $
adpcm_buf        db      SIZE_ADPCM/2 dup (0)
half_adpcm EQU $
                db       SIZE_ADPCM/2 dup (0)
last_buff_cnt   dw       0
app_dma_seg     dw       0
app_dma_off     dw       0
app_dma_count   dw       0
app_adpcm_count dw       0
adpcm_ref       db       0

dsp_table       label   word

NUM_DSP_COMMANDS equ    37

; Command 00 - Silence mode - Command 80
                db      00h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_silence
; Command 10 - Direct 8 bit DAC - single byte transfer
                db      10h
                db      1         ; 1 byte of arguments
                dw        offset dsp_direct_dac
; Command 11 - DMA high speed 8 bit DAC mode - Command 91
                db      11h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_8bit_high_dma_dac
; Command 14 - DMA mode 8 bit DAC
                db      14h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_8bit_dma_dac
; Command 16 - DMA mode 2 bit ADPCM DAC
                db      16h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_8bit_dma_dac
;                dw        offset dsp_2bit_adpcm_dac
; Command 17 - DMA mode 2 bit ADPCM DAC with REFERENCE byte
                db      17h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_8bit_dma_dac
; Command 18 - DMA high speed 8 bit looped DMA
                db      18h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_8bit_high_loop_dma_dac
; Command 19 - DMA high speed 8 bit ADC mode - Command 99
                db      19h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_8bit_high_adc
; Command 1C - 8-bit auto-init DMA playback
                db      1Ch
                db      0         ; ???? bytes of arguments
                dw        offset dsp_auto_play
; Command 20 - Direct mode ADC - single byte transfer
                db      20h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_direct_adc
; Command 24 - DMA mode ADC
                db      24h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_dma_adc
; Command 2C - 8-bit auto-init DMA recording
                db      2Ch
                db      0         ; ???? bytes of arguments
                dw        offset dsp_auto_record
IFDEF NEVER
; Command 30 - MIDI read - polling mode
                db      30h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_midi_read_poll
; Command 31 - MIDI read - interrupt mode
                db      31h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_midi_read_intr
; Command 34 - MIDI uart mode - polling mode
                db      34h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_midi_uart_poll
; Command 35 - MIDI uart mode - interrupt mode
                db      35h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_midi_uart_int
; Command 38 - MIDI write - polling mode
                db      38h
                db      1         ; 1 byte of arguments
                dw        offset dsp_midi_write_poll
ENDIF
; Command 40 - Set time constant - (sample rate)
                db      40h
                db      1         ; 1 byte of arguments
                dw        offset dsp_time_const
; Command 41 - Set Frequency
                db      41h
                db      2         ; 1 byte of arguments
                dw        offset dsp_frequency
; Command 46 - Unknown cmd
                db      46h
                db      0         ; 1 byte of arguments
                dw        offset dsp_unknown
; Command 48 - Set block size
                db      48h
                db      2         ; 2 bytes of arguments
                dw        offset dsp_set_block_size
; Command 50 - Halt DMA - Command D0
                db      050h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_halt_dma
; Command 51 - Turn on speaker - Command D1
                db      051h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_speaker_on
; Command 53 - Turn off Speaker - Command D3
                db      053h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_speaker_off
; Command 54 - Continue DMA - Command D4
                db      054h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_cmd_dma_continue
; Command 58 - Get speaker status - Command D8
                db      058h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_speaker_status
; Command 5A - Pause speaker until next dma xfer - Undocumented
                db      05Ah
                db      0         ; 0 bytes of arguments
                dw        offset dsp_stop_auto_play
; Command 60 - Negation - Undocumented
                db      60h
                db      1         ; ???? bytes of arguments
                dw        offset dsp_negation
; Command 61 - Get DSP version - Command E1
                db      061h
                db      0         ; 0 bytes of arguments
                dw        offset dsp_version
; Command 62 - Encoded - Undocumented
                db      62h
                db      1         ; ???? bytes of arguments
                dw        offset dsp_encoded
; Command 64 - Return Data - Undocumented
                db      64h
                db      1         ; ???? bytes of arguments
                dw        offset dsp_return_data
; Command 68 - Echo Back - Undocumented
                db      68h
                db      0         ; ???? bytes of arguments
                dw        offset dsp_echo_back
; Command 72 - Generate IRQ back to application - Undocumented
                db      72h
                db      0         ; ???? bytes of arguments
                dw        offset dsp_gen_irq
; Command 74 - DMA mode 4 bit ADPCM DAC
                db      74h
                db      2         ; 0 bytes of arguments
                dw        offset dsp_4bit_adpcm_dac
; Command 75 - DMA mode 4 bit ADPCM DAC with reference byte
                db      75h
                db      2         ; 0 bytes of arguments
                dw        offset dsp_4bit_adpcm_dac_ref
; Command 76 - DMA mode 2.6 bit ADPCM DAC
                db      76h
                db      2         ; 0 bytes of arguments
                dw        offset dsp_8bit_dma_dac
; Command 77 - DMA mode 2.6 bit ADPCM DAC with reference byte
                db      77h
                db      2         ; 0 bytes of arguments
                dw        offset dsp_8bit_dma_dac
IFDEF NEVER
; Command 78 - Reset Buffer - Undocumented
                db      78h
                db      0         ; ???? bytes of arguments
                dw        offset dsp_reset_buffer
ENDIF
_DATA ends

_TEXT           segment byte public use16 'CODE'
assume          cs:DGROUP, ds:DGROUP, ss:DGROUP

;---------------------------------------------------------------------
;
; SIMULATE_IRQ_SB - this function will fo to the apps interrupt handler
;          via a SW INT rather than a HW IRQ for DSP (dma tc) irqs.
;
;---------------------------------------------------------------------
simulate_sb_irq proc near
    db 0cdh        ; int opcode
_fake_sb_irq db 08h+05h    ; interrupt #
    ret
simulate_sb_irq endp

;---------------------------------------------------------------------
;
; IRQ_SB - this function will dispatch to the apps interrupt handler
;          for DSP (dma tc) irqs.
;
;---------------------------------------------------------------------
force_sb_irq proc near
    push ax
    push dx

    mov dx,_reg_index    ; should use equate here ....
    mov al,IW_IEIRQI
    out dx,al
    mov dx,_reg_data_high
    in al,dx
    jmp $+2
    and al, NOT 01h        ; and back low again
    out dx,al
    jmp $+2
    or al,01h            ; then toggle high ...
    out dx,al
    jmp $+2

    mov version_count,3    ; so we won't do much in 2xe nmi handler
    call enable_2xe_nmi

; LEAVE line high !!!! . Read of 2xE will make it go low ....

not_enabled:
    pop dx
    pop ax
    ret
force_sb_irq endp

;---------------------------------------------------------------------
; Utility function to start up a DMA playback xfer
;---------------------------------------------------------------------
start_dac_dma_xfer proc near
    call near ptr dsp_speaker_on
    call near ptr _SetPic
start_dac_dma_xfer1:
;    call near ptr change_from_pio
    call near ptr dsp_prog_dac_block_size
    call near ptr dsp_dac_dma_continue        ; tell DMA to go 
    call near ptr change_from_pio
    ret
start_dac_dma_xfer endp


;---------------------------------------------------------------------
; proc_2xE_read()
;
; DSP data-available status - when MSB is 1, DSP has data ready on 2xC
; Also read by application to acknowledge SB interrupt.
;
; Assumes DS is CS
;---------------------------------------------------------------------
proc_2xE_read proc near
IFDEF D_SB
    mov dx,22ah    ; This should use variable .....
    in al,dx
    mov dl,R2xER_CODE
    call near ptr _add_to_debug_table
ENDIF
    call near ptr reset_2xf        ; reset IRQ

    mov dx,_reg_index    ; should use equate here ....
    mov al,IW_IEIRQI
    out dx,al
    mov dx,_reg_data_high
    in al,dx
    jmp $+2
    and al, NOT 01h        ; toggle low like real SB ....
    out dx,al

    ;
    ; This version count thing will make the application read a zero from 2xE
    ; before it reads the minor version number from the card.  This makes 
    ; it wait like a real blaster.
    ;
    cmp version_count,3
    je short send_default
    cmp version_count,1
    je short send_zero
    cmp version_count,2
    je short send_minor
    jmp send_default         ; otherwise do the default data handshaking
send_zero:
    ;
    ; App sent command and read 2xE
    ; we'll stop him by turning off the handshaking bit
    ;
    mov version_count,2
    mov dx,_reg_sb2xE             ; data not ready handshaking
    mov al,00H
    out dx,al
    ret

send_minor:
    ;
    ; Now, app read the 0 and wants to read the last minor byte
    ; reenable handshaking bit and let him read the data
    ;
    call near ptr flip_on
    mov al,_dsp_min_ver
    call near ptr send_byte_to_app
    ; There was code in here to enable master CODEC irq's...


    mov version_count,0



    ret

send_default:
    call near ptr disable_2xe_nmi ; disable nmis
    ret
proc_2xE_read endp

;---------------------------------------------------------------------
;---------------------------------------------------------------------
disable_2xe_nmi proc near
    mov dx,_reg_2xFcontrol
    and _regctl_mask,07FH
    mov al,_regctl_mask
    out dx,al
    ret
disable_2xe_nmi endp

;---------------------------------------------------------------------
;---------------------------------------------------------------------
enable_2xe_nmi proc near
    test _shared,STAT_WINDOWS_RUNNING
    jne enable_windows_running

    mov dx,_reg_2xFcontrol
    or _regctl_mask,80h
    mov al,_regctl_mask
    out dx,al
enable_windows_running:
    ret
enable_2xe_nmi endp

;---------------------------------------------------------------------
;---------------------------------------------------------------------
send_byte_to_app proc near
    mov dx,_reg_update_sb2xa
    out dx,al                      ; load 2xA with byte
    call near ptr enable_2xe_nmi   ; enable 2xE NMIs

    mov dx,_reg_sb2xE              ; status send ready - from sbos to app
    mov al,080H
    out dx,al
    ret
send_byte_to_app endp

;---------------------------------------------------------------------
; proc_2x6_write()
;
; This handles ALL writes to the 2x6 port
; Assumes DS is CS
;
;---------------------------------------------------------------------
proc_2x6_write proc near
IFDEF D_SB
    mov al,0                ; don't care what was written
    mov dl,R2x6W_CODE
    call near ptr _add_to_debug_table
ENDIF
    call near ptr reset_SB           ; reset the IRQ

    mov dsp_status,0        ; clear all flags

    mov al,0                ; in case codec not there ....
    test _shared,STAT_CODEC_ENABLED
    je no_codec

IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_INIT_MARK
    call near ptr _add_to_debug_table
ENDIF

    mov _dsp_dma_size,07ffH  ; reset dma count to default
    mov _dsp_direct_mode,0
    mov _dsp_crypt_seed,40H
    mov version_count,0

    mov dx,_reg_codec_addr
    mov al,PIN_CTRL
    out dx,al
    inc dx
    in al,dx
    or al,IRQ_ENABLE
    out dx,al

; lets set up a default rate ....
    mov _dsp_old_dma_rate,0ffffH ; reset prev freq
    mov al,0E0H
    mov dsp_args,al
    call near ptr dsp_time_const

    call near ptr change_from_pio

    call near ptr stop_timer

    call near ptr dsp_halt_dma

    call near ptr _SetPic

    call near ptr _reset_synth        ; use this opportunity to reset the synth 
                                      ; also

    call near ptr flip_on            ; turn bit flipping on

    mov dx,_reg_sb2xc
    in al,dx                ; make sure the bit is alway OFF when exiting here
    test al,80h
    jnz bits_on
    in al,dx                ; make THIS one on, so apps next is off
bits_on:

    mov al,0aah
    jmp codec_1

no_codec:
    call dsp_speaker_on
    mov al,0
codec_1:
    call near ptr send_byte_to_app
    ret
proc_2x6_write endp

;---------------------------------------------------------------------
; proc_2xC_write()
; Assumes DS is CS
;
; This handles ALL writes to the 2xC port (SB DSP command and data)
; Data written to this port is a command for the SoundBlaster DSP and any
; associated data.  When a command is received, dsp_status gets set to BUSY,
; the command gets found in the command table. Variables get setup:
;   dsp_args_needed - offset 1 after command in table - number of args needed
;   dsp_com_offset  - offset of found command in command table - faster access
;   dsp_args_got    - number of arguments received already - 0
; As data gets received, the variables get updated and the appropriate function
; gets called when all the data has arrived (offset 2 in the command table).  
; The arguments get stored in the variable dsp_args which is readable by 
; the individual functions.
;
; NOTE:
;   Since we read from the same port that the app reads from to test read/write
;   ready status (2xC MSB), we must turn off the bit flipping while the app
;   writes data.  After we read the data, we must write MSB=0 to 2xC so app 
;   thinks DSP is ready to receive.  Once all data has been received, the bit
;   flipping is re-enabled. 
;
;---------------------------------------------------------------------
proc_2xC_write proc near
    call near ptr reset_SB
    mov dx,_reg_sb2xC     ; Data written to 2xC by application
    in al,dx              ; Read value
    test dsp_status,BUSY  ; is the status busy
    je short find_command ; if not - find the command in the table
    ; 
    ; already got a command - this data is an argument to the last command
    ;
IFDEF D_SB
    mov dl,R2xCW_CODE
    call near ptr _add_to_debug_table
ENDIF
    mov si,offset dsp_args;
    mov bl,dsp_args_got
    xor bh,bh
    mov [si+bx],al        ; save argument in table - Zero based
    inc dsp_args_got      ; add this argument to the counter
    mov dx,_reg_2xC_write ; 2xD
    mov al,0H             ; clear bit 7
    out dx,al             ; out it
    mov dl,dsp_args_got   ; number of received arguments
    cmp dl,dsp_args_needed; compare with number in table
    jne short proc_2xC_end; if I need more, I can leave
    ;
    ; this was the last argument to the previous command
    ;
    and dsp_status,NOT BUSY   ; next data is command, bit reset, status = IDLE
    jmp short call_dsp_func ; call the function to execute the command
    ;
    ; Search through the table until one command matches al
    ; ???? What do I do if I can't find one
    ;
find_command:
    and al,7fh            ; mask off top bit (it flips)
IFDEF D_SB
    mov dl,R2xCW_CODE
    call near ptr _add_to_debug_table
ENDIF
    mov si, offset dsp_table ; get table offset
    mov cx, NUM_DSP_COMMANDS ; How many commands to look through
    mov bx, 0                ; start at the beginning
top_loop:
    cmp al,[si+bx]           ; test against known command from table
    je short found_command
    add bx,4                 ; index to next entry in table
    loop top_loop            ; decrement cx and loop to top if (9cx != 0)
    ; 
    ; command was not found - Just leave - I guess??
    ;
    jmp short proc_2xC_end
found_command:
    mov dl,[si+bx+1]        ; dl = number of arguments needed
    mov dsp_com_offset,bl   ; save old offset
    cmp dl,0                ; see if there are any arguments to get
    je short call_dsp_func  ; There are no arguments needed - call function
    ;
    ; Setup so next byte coming in is argument for this command
    ;
    or dsp_status,BUSY      ; set status to busy
    mov dsp_args_needed,dl  ; save # args needed
    mov dsp_com_offset,bl   ; save old offset
    mov dsp_args_got,0      ; clear argument counter
    call near ptr flip_off  ; shut off bit flipping
    jmp short proc_2xC_end  ; Outta here
    ;
    ; This calls the appropriate DSP function with all related arguments
    ;
call_dsp_func:
    call near ptr flip_on   ; turn bit flipping back on
    mov si, offset dsp_table; bx = offset table
    mov bl, dsp_com_offset
    xor bh,bh
    call word ptr [si+bx+2] ; OH yeah!!
proc_2xC_end:
    ret
proc_2xC_write endp

;---------------------------------------------------------------------
; reset_2xf()
;
; Resets chip after Generation of IRQ via MPU-401 ports (general register)
; or read from 2xE
; Assumes DS is CS
;
; Write to 2xbi5 clears IRQs generated via ports listed in 2xF
; OUT to reg_2xFcontrol (2xF) = 0x05 & 0x58 = 0x5D
; OUT to reg_mixer (2x0) to enable write to 2xB
; OUT to reg_2xBindexed (2xB) = 00 to clear
; OUT to reg_2xFcontrol (2xF) = regctl_mask to set index bits back to 0
;
; ???? NOTE: 2x0 write to enable 2xB will disappear in IW chip -- CHANGE THESE
;---------------------------------------------------------------------
reset_2xf proc near
    push cx
    mov cx,3
x2xF_loop:
    mov dx,_reg_2xFcontrol
    mov al,_regctl_mask     ; preserve 2xF state
    or al,05h               ; select register 5
    out dx,al
    mov dx,_reg_mixer       ; enable write to 2xB
    mov al,_mixer_mask
    out dx,al
    mov dx,_reg_2xBindexed 
    mov al,00h
    out dx,al               ; clear interrupt
    mov dx,_reg_2xFcontrol
    mov al,_regctl_mask     ; preserve 2xF state
    out dx,al               ; set 0 back in index bits
    loop x2xF_loop
    pop cx
    ret
reset_2xf endp

;---------------------------------------------------------------------
; flip_on()
;
; enables bit flipping on the MSB of 2xC
;---------------------------------------------------------------------
flip_on proc near
    mov dx,_reg_2xFcontrol    ; 2xF
    mov al,_regctl_mask       ; 2xF mask
    or  al,20h                ; Turn on bit flipping - bit 5
    mov _regctl_mask,al       ; store the new mask
    out dx,al                 ; out it
    ret
flip_on endp

;---------------------------------------------------------------------
; flip_off()
;
; disables bit flipping on the MSB of 2xC
;---------------------------------------------------------------------
flip_off proc near
    mov dx,_reg_2xFcontrol   ; 2xF
    mov al,_regctl_mask      ; 2xF mask
    and al,0dfh              ; Turn off bit flipping - bit 5
    mov _regctl_mask,al      ; store the mask back
    out dx,al                ; out it
    mov dx,_reg_2xC_write    ; 2xD
    mov al,0H                ; clear bit 7
    out dx,al                ; out it
    ret
flip_off endp

;---------------------------------------------------------------------
; convert_second_seed
; ENTER:
;     BL = data value
;     DL = seed value
; EXIT:
;     DL = new byte .....
;
; Do CL encryption stuff
;if( data & 0x01 )
;    seed--;
;if( data & 0x02 )
;    seed += 0x2;
;if( data & 0x04 )
;    seed -= 0x4;
;if( data & 0x08 )
;    seed += 0x8;
;if( data & 0x10 )
;    seed += 0x10;
;if( data & 0x20 )
;    seed -= 0x20;
;if( data & 0x40 )
;    seed += 0x40;
;if( data & 0x80 )
;    seed += 0x80;
;---------------------------------------------------------------------
convert_second_seed    proc    near
    test    bl,1
    je    short @1@86
    dec    dl
@1@86:
    test    bl,2
    je    short @1@142
    add    dl,2
@1@142:
    test    bl,4
    je    short @1@198
    add    dl,252
@1@198:
    test    bl,8
    je    short @1@254
    add    dl,8
@1@254:
    test    bl,16
    je    short @1@310
    add    dl,16
@1@310:
    test    bl,32
    je    short @1@366
    add    dl,224
@1@366:
    test    bl,64
    je    short @1@422
    add    dl,64
@1@422:
    test    bl,128
    je    short @1@478
    add    dl,128
@1@478:
    ret    
convert_second_seed    endp

;---------------------------------------------------------------------
; convert_first_seed
; ENTER:
;     BL = data value
;     DL = seed value
; EXIT:
;     DL = new byte .....
;
; Do CL encryption stuff
;if( data & 0x01 )
;    seed++;
;if( data & 0x02 )
;    seed -= 0x2;
;if( data & 0x04 )
;    seed -= 0x4;
;if( data & 0x08 )
;    seed += 0x8;
;if( data & 0x10 )
;    seed -= 0x10;
;if( data & 0x20 )
;    seed += 0x20;
;if( data & 0x40 )
;    seed += 0x40;
;if( data & 0x80 )
;    seed += 0x80;
;---------------------------------------------------------------------
convert_first_seed    proc    near
    test    bl,1
    je    short @2@86
    inc    dl
@2@86:
    test    bl,2
    je    short @2@142
    add    dl,254
@2@142:
    test    bl,4
    je    short @2@198
    add    dl,252
@2@198:
    test    bl,8
    je    short @2@254
    add    dl,8
@2@254:
    test    bl,16
    je    short @2@310
    add    dl,240
@2@310:
    test    bl,32
    je    short @2@366
    add    dl,32
@2@366:
    test    bl,64
    je    short @2@422
    add    dl,64
@2@422:
    test    bl,128
    je    short @2@478
    add    dl,128
@2@478:
    ret    
convert_first_seed    endp

;***************************************************************************
;
; DSP Jump Procedures
;
; These Procedures are jumped to via a write to 2xC through 'dsp_table'
;
;***************************************************************************
; Command 0x80 (or 0x00)
; Play silence data for a while. 
; dsp_args - LSB
; dsp_args+1 - MSB
; set up a timer for the same amount of time and them tell app
;***************************************************************************
dsp_silence proc near
    or dsp_status,SILENCE_DELAY
    mov bx,100
    call near ptr start_timer
    ret
dsp_silence endp

;***************************************************************************
; Command 0x10
; Polled I/O - Direct playback
;***************************************************************************
dsp_direct_dac proc near
    ;
    ; NOTE:only use regs AX and DX
    ;
    or dsp_status,COMMAND_10

    cmp _dsp_direct_mode,1
    je short already_direct
    mov _dsp_direct_mode,1

    call near ptr change_to_pio

already_direct:
    ret
dsp_direct_dac endp

;***************************************************************************
; Command 0x91 (or 0x11)
; High speed DMA playback
;***************************************************************************
dsp_8bit_high_dma_dac proc near
    or dsp_status,HIGH_SPEED
    call dsp_time_const
    jmp start_dac_dma_xfer
dsp_8bit_high_dma_dac endp

;***************************************************************************
; Command 0x14
; 8 bit normal DMA playback
;***************************************************************************
dsp_8bit_dma_dac proc near
;    call near ptr change_from_pio
    cmp _dsp_direct_mode,1
    jne short not_direct
    mov _dsp_direct_mode,0
;    call near ptr change_from_pio
not_direct:
    call near ptr _SetPic
    call near ptr dsp_set_block_size        ; set upper and lower count size
    jmp  start_dac_dma_xfer1                     ; share some code ....

dsp_8bit_dma_dac endp

ifdef NEVER
;***************************************************************************
; Command 0x16
; 2-bit ADPCM playback (no reference byte)
;***************************************************************************
dsp_2bit_adpcm_dac proc near
    ret
dsp_2bit_adpcm_dac endp

;***************************************************************************
; Command 0x17
; 2-bit ADPCM playback (with reference byte)
;***************************************************************************
dsp_2bit_adpcm_dac_ref proc near
    ret
dsp_2bit_adpcm_dac_ref endp
endif

;***************************************************************************
; Command 0x98 (or 0x18)
; High speed looped DMA playback
;***************************************************************************
dsp_8bit_high_loop_dma_dac proc near
    or dsp_status,HIGH_SPEED
    call near ptr dsp_time_const
    call near ptr dsp_auto_play
    ret
dsp_8bit_high_loop_dma_dac endp

;***************************************************************************
; Command 0x99 (or 0x19)
; High speed DMA sampling
;***************************************************************************
dsp_8bit_high_adc proc near
    or dsp_status,HIGH_SPEED
    call near ptr dsp_time_const
    jmp start_adc_dma_xfer
dsp_8bit_high_adc endp

;***************************************************************************
; Command 0x1C
; 8-bit auto init DMA playback
;***************************************************************************
dsp_auto_play proc near
    or dsp_status,AUTOINIT_MODE
    jmp start_dac_dma_xfer
dsp_auto_play endp

;***************************************************************************
; Command 0x2C
; 8-bit auto init DMA record
;***************************************************************************
dsp_auto_record proc near
    or dsp_status,AUTOINIT_MODE
    jmp start_adc_dma_xfer
dsp_auto_record endp

;***************************************************************************
;
;***************************************************************************
dsp_direct_adc proc near
    ret
dsp_direct_adc endp
;***************************************************************************
;
;***************************************************************************
dsp_dma_adc proc near
    call near ptr dsp_set_block_size        ; set upper and lower count size
start_adc_dma_xfer:
    call near ptr _SetPic
    call near ptr dsp_prog_adc_block_size
    call near ptr dsp_adc_dma_continue        ; tell DMA to go 
    call near ptr change_from_pio
    ret
dsp_dma_adc endp
IFDEF NEVER
;***************************************************************************
;
;***************************************************************************
dsp_midi_read_poll proc near
    ret
dsp_midi_read_poll endp
;***************************************************************************
;
;***************************************************************************
dsp_midi_read_intr proc near
    ret
dsp_midi_read_intr endp
;***************************************************************************
;
;***************************************************************************
dsp_midi_uart_poll proc near
    ret
dsp_midi_uart_poll endp
;***************************************************************************
;
;***************************************************************************
dsp_midi_uart_int proc near
    ret
dsp_midi_uart_int endp
;***************************************************************************
;
;***************************************************************************
dsp_midi_write_poll proc near
    ret
dsp_midi_write_poll endp
ENDIF

;***************************************************************************
; Command 0x40
; Set the time constant (DMA rate)
; dsp_arg[0] is time constant param
;***************************************************************************
dsp_time_const proc near
    xor ecx,ecx
    xor edx,edx              ; Dividend is EDX:EAX

    test dsp_status,HIGH_SPEED
    je short low_speed_dma
; high speed  = TC = 65536 - (256,000,000/rate)
; i.e rate = 256,000,000/(65536-TC)
    mov eax,256000000
    mov ebx,65536
    mov ch,old_time_const    ; put in UPPER byte ....
    jmp short calc_rate

low_speed_dma:
; low speed  = TC = 256 - (1,000,000/rate)
; i.e rate = 1,000,000/(256-TC)
    mov cl,dsp_args
    mov old_time_const,cl
    mov eax,1000000
    mov    ebx,256              ; get 256 - time constant
calc_rate:
    sub ebx,ecx
    je short div_by_zero
    idiv ebx                  ; Divide 1000000 by 256-TC
    mov _dsp_dma_rate,ax      ; store quotient dma rate

div_by_zero:
set_rate:
    call near ptr _set_dma_rate        ; let a C function do the work

    ret
dsp_time_const endp

;***************************************************************************
; Command 0x41
; Set the frequency
; dsp_arg[0] is high byte of freq
; dsp_arg[1] is low byte of freq
;***************************************************************************
dsp_frequency proc near
    mov ah,dsp_args
    mov al,dsp_args+1
    call near ptr _set_dma_rate        ; let a C function do the work
    ret
dsp_frequency endp


dsp_unknown proc near
    ret
dsp_unknown endp
;***************************************************************************
; Input BX = counter value to program
;***************************************************************************
prog_codec_play_ctr proc near
    mov dx,_reg_codec_addr
    mov al,PLY_LWR_CNT
    or  al,trd_bit
    out    dx,al
    inc    dx
    mov al,bl
    out    dx,al

    mov dx,_reg_codec_addr
    mov al,PLY_UPR_CNT
    or  al,trd_bit
    out    dx,al
    inc    dx
    mov al,bh
    out    dx,al
    ret
prog_codec_play_ctr endp
;***************************************************************************
;
;***************************************************************************
dsp_prog_dac_block_size proc near
IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_PROG_BLOCK_MARK
    call near ptr _add_to_debug_table
    call near ptr debug_dma
ENDIF

;    call dsp_halt_dma

    mov bx,_dsp_dma_size

; Please note that these are all special cases. It would be NICE not
; to need these, but unfortunately that isn't the case ...

; This is for games like NHL Hockey 95 that puts the MAX (FFFF) tothe DSP
; but only puts 1fe to the PC's DMA controller.....
    cmp bx,0fff0h
    jae no_bump

    mov dx,3
    in al,dx
    jmp $+2
    mov ah,al
    in al,dx
    xchg al,ah

; if the PC DMA count is at -1, just let it thru
    cmp ax,0FFFFH
    je short no_bump

; if the PC DMA counter is more than DSP count, let it thru since we are
; gauranteed to get a DMA TC IRQ. If its less than PC count, its possible
; that we won't have enough counts to get an IRQ from the CODEC counters.
; IF the PC DMA controller is in autoinit mode, THEN we will have enough
; counts since the DMA controller will continue after the count wraps at FFFF.
; Unfortunately, we can't read the mode of the DMA controller so we can't
; tell if thats the case ..... so this prevents a lock up waiting 
; for a TC that will never happen ...
    cmp ax,bx
    ja short no_bump

; if the PC DMA count is small enough, just let it thru
; This helps some of the card detection routines ....
;    cmp ax,16
;    jb short no_bump

    mov bx,ax    ; replace _dsp_dma_size with whats in the PC DMA controller.

no_bump:
IFDEF D_SB
    mov dl,MARKER_CODE
    mov al,bl
    call near ptr _add_to_debug_table
    mov dl,MARKER_CODE
    mov al,bh
    call near ptr _add_to_debug_table
ENDIF
    call prog_codec_play_ctr
    ret
dsp_prog_dac_block_size endp

;***************************************************************************
;
;***************************************************************************
dsp_prog_adc_block_size proc near
IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_ADC_BLOCK_MARK
    call near ptr _add_to_debug_table
    call near ptr debug_dma
ENDIF
;    call dsp_halt_dma

    mov bx,_dsp_dma_size

    mov dx,_reg_codec_addr
    mov al,REC_LWR_CNT
    or  al,trd_bit
    out dx,al
    inc    dx
    mov al,bl
    out dx,al

    mov dx,_reg_codec_addr
    mov al,REC_UPR_CNT
    or  al,trd_bit
    out dx,al
    inc    dx
    mov al,bh
    out dx,al
    ret
dsp_prog_adc_block_size endp

;***************************************************************************
;
;***************************************************************************
IFDEF D_SB
debug_dma proc near
ifdef D_SB
    mov dl,DMA_DEBUG

    out 0ch,al    ; clear FF
    jmp $+2

    in al,83H    ; page
    call near ptr _add_to_debug_table

    in al,2            ; LSB addr
    call near ptr _add_to_debug_table

    in al,2            ; MSB addr
    call near ptr _add_to_debug_table

    in al,3            ; LSB count
    call near ptr _add_to_debug_table

    in al,3            ; MSB count
    call near ptr _add_to_debug_table
endif
    ret
debug_dma endp
ENDIF

;***************************************************************************
;
;***************************************************************************
dsp_set_block_size proc near
IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_SET_BLOCK_MARK
    call near ptr _add_to_debug_table
    call near ptr debug_dma
ENDIF
    mov al,dsp_args
    mov ah,dsp_args+1


;    sub ax,1





; CAREFUL HERE FOLKS !!!!!!
    cmp ax,0
    je short large_enough

    cmp ax,16
    ja  short large_enough

;    cmp ax,08
;    jb  short large_enough

    call near ptr force_sb_irq

large_enough:
    mov _dsp_dma_size,ax        ; save it just in case ....
    ret
dsp_set_block_size endp

;***************************************************************************
;
;***************************************************************************

dsp_halt_dma proc near
    mov dx,_reg_codec_addr
    mov al,IFACE_CTRL
    or  al,trd_bit
    out dx,al
    inc    dx
    in  al,dx
;   and al,0FEH                 ; PEN off
    and al,0FCH                 ; PEN and CEN  off
    out dx,al                   ; disable playback enable

    and dsp_status,NOT(ADPCM_MODE+NEXT_2_LAST+ADPCM_BUFF2+DONE_ADPCM)

IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_HALT_MARK
    call near ptr _add_to_debug_table
    call near ptr debug_dma
ENDIF
    ret
dsp_halt_dma endp
;***************************************************************************
;
;***************************************************************************

dsp_speaker_on proc near
    mov bl,0
    jmp do_speaker
dsp_speaker_on endp

dsp_speaker_off proc near
    mov bl,80h
;    jmp do_speaker      ; let it fall thru ..... CAREFULL !!!!!
dsp_speaker_off endp

do_speaker proc near
    mov al,LEFT_OUTPUT
    call near ptr do_mutes
    mov al,RIGHT_OUTPUT
    call near ptr do_mutes
    ret
do_speaker endp

do_mutes proc near
    mov dx,_reg_codec_addr
    or  al,trd_bit
    out dx,al
    inc    dx
    in al,dx
    and al,07FH
    or al,bl
    out dx,al
    ret
do_mutes endp
;***************************************************************************
;
; Enter cl = PLAYBACK_ENABLE or CAPTURE ENABLE
;***************************************************************************
dma_continue proc near

    mov trd_bit,0

    call stop_timer            ; just in case its left running ....
    mov dx,_reg_codec_status
    in al,dx

    cmp cl,CAPTURE_ENABLE
    je short not_sierra        ; sierra uses playback, not capture

IFDEF NEVER
    cmp _dsp_dma_size,16
    ja short not_sierra
ENDIF
    cmp _dsp_dma_size,0
    jne short not_sierra

IFDEF D_SB
    mov dl,MARKER3_CODE
    mov al,bl
    call near ptr _add_to_debug_table
ENDIF
    or dsp_status,PULSE_SBIRQ    ; get this IRQ to me !!!!
    mov bx,05    ; 50 microsec timer ...
    call near ptr start_timer

    jmp short done_cont        ; don't start yet ...

not_sierra:

    test dsp_status,AUTOINIT_MODE   ; only stop if NOT in autoinit mode ....
    jne short no_trd
    mov trd_bit,CODEC_TRD
no_trd:
    mov dx,_reg_codec_addr
    mov al,IFACE_CTRL
    or  al,trd_bit
    out dx,al
    inc    dx
    in  al,dx
    or  al,cl            ; enable playback or capture
    out dx,al
done_cont:
    ret
dma_continue endp

;***************************************************************************
;
;***************************************************************************
dsp_cmd_dma_continue proc near
    test dsp_status,AUTOINIT_MODE   ; only continue if NOT in autoinit mode ....
    jne short no_continue
    cmp _dsp_dma_size,0
    je no_continue
dsp_dac_dma_continue:
    mov cl,PLAYBACK_ENABLE
    call near ptr dma_continue
no_continue:
    ret
dsp_cmd_dma_continue endp

;***************************************************************************
;
;***************************************************************************
dsp_adc_dma_continue proc near
    cmp _dsp_dma_size,0
    jne no_force_it
    call near ptr force_sb_irq
no_force_it:
    mov cl,CAPTURE_ENABLE
    call near ptr dma_continue
    ret
dsp_adc_dma_continue endp
;***************************************************************************
;
;***************************************************************************
dsp_speaker_status proc near
    ret
dsp_speaker_status endp
;***************************************************************************
;
;***************************************************************************
dsp_stop_auto_play proc near
    and dsp_status,NOT AUTOINIT_MODE
    ret
dsp_stop_auto_play endp
;***************************************************************************
;
;***************************************************************************
dsp_negation proc near
    mov al,dsp_args
    xor al,0ffH
    call near ptr send_byte_to_app
    ret
dsp_negation endp
;***************************************************************************
;
;***************************************************************************
dsp_version proc near
    mov al,_dsp_maj_ver
    call near ptr send_byte_to_app
    mov version_count,1
    ret
dsp_version endp
;***************************************************************************
;
;***************************************************************************
dsp_encoded proc near
    mov bl,dsp_args
    mov dl,_dsp_crypt_seed
    test dsp_status,GOT_FIRST_CRYPT
    jne short do_second
    or dsp_status,GOT_FIRST_CRYPT
; OK do conversion of first byte ....
    call near ptr convert_first_seed
    mov al,dl
    mov bl,dsp_args
    mov dl,0E5H
    call near ptr convert_first_seed
    mov _dsp_crypt_seed,dl
; OK al has data. Now where do I put it ????
; poke it down to begining of buffer ....
    mov dsp_first_byte,al
    jmp short done_crypt
do_second:
    xor dsp_status,GOT_FIRST_CRYPT
    call near ptr convert_second_seed
    mov al,dl
    mov dl,0E5H
    call near ptr convert_second_seed
    mov _dsp_crypt_seed,dl
; OK al has data. Now where do I put it ????
    mov dsp_second_byte,al
; Now figure out where its supposed to go in PC RAM ...
    push es
    xor ax,ax    ; prep for building segment of phys. ram addr
;
    in al,2        ; get lsb
    mov di,ax    ; save it
    in al,2        ; get msb
    mov bl,al    
    in al,83h    ; first get page
    mov bh,al
; bx is page + msb of addr (12 bits)
    shl bx,4    ; shift to upper 12 bits
    push bx
    pop es        ; es:di = pointer to phys addr
    mov al,dsp_first_byte
    mov es:[di],al
    mov al,dsp_second_byte
    mov es:[di+1],al
; now zero out DMA count
    mov al,0ffh
    out 3,al
    jmp $+2
    out 3,al
    pop es

done_crypt:
    mov dx,_reg_update_sb2xa
    out dx,al
    call near ptr flip_on
    ret
dsp_encoded endp
;***************************************************************************
;
;***************************************************************************
dsp_return_data proc near
    mov al,dsp_args
    mov _dsp_ack_data,al
    ret
dsp_return_data endp
;***************************************************************************
;
;***************************************************************************
dsp_echo_back proc near
    mov al,_dsp_ack_data
    call near ptr send_byte_to_app
    call near ptr disable_2xe_nmi ; disable nmis
    ret
dsp_echo_back endp
;***************************************************************************
;
;***************************************************************************
dsp_gen_irq proc near
    call near ptr force_sb_irq
    ret
dsp_gen_irq endp
;***************************************************************************
;
; Enter w/CX set to amount of data to send
; Enter w/BX set to offset to data to play
;***************************************************************************
prog_pcdma_adpcm_ctr proc near
; program PC DMA controller's counter
    mov al,05h    ; disable DMA 1
    out 0ah,al
    jmp $+2

; Now re-program the PC's DMA controller to point to my buffer ....
    xor eax,eax
    mov ax,ds
prog_dma_stuff:
    shl eax,4
    and ebx,0ffffh
    add eax,ebx

    out 0ch,al    ; clear FF
    jmp $+2

    out 2,al    ; lsb addr
    jmp $+2

    shr eax,8
    out 2,al    ; msb addr
    jmp $+2

    shr eax,8
    out 83h,al    ; page
    jmp $+2

;    mov al,59h
    mov al,49h
    out 0bh,al    ; mode
    jmp $+2

    out 0ch,al    ; clear FF
    jmp $+2

    mov al,cl
    out 3,al    ; lsb count
    jmp $+2

    mov al,ch
    out 3,al    ; msb count
    jmp $+2

    mov al,01h
    out 0ah,al        ; enable dma channel ...

    ret
prog_pcdma_adpcm_ctr endp

;************************************************************************
; EXIT with:
; ES:DI = apps compressed data buffer
; app_adcpm_count = amount of data to decompress & then give IRQ
; app_dma_count = size of ADPCM buffer in PC (DMA buffer)
;************************************************************************
build_pc_ptr proc near
    out 0ch,al    ; clear FF
    jmp $+2
; First get the count from the PC dma controller and save it
    in al,3
    mov ah,al
    in al,3
    xchg al,ah
    inc ax
    mov app_dma_count,ax
; Also save the count that the DSP was told to decompress & play 
; (This could be different in case they set up a big PC buffer and a
;  told the DSP to play a smaller amount and give an IRQ back ...)
    mov ax,_dsp_dma_size
    mov app_adpcm_count,ax

; figure out where its supposed to go in PC RAM ...
    xor ax,ax       ; prep for building segment of phys. ram addr
;
    in al,2         ; get lsb
    mov di,ax       ; di = offset 

    in al,2         ; get msb
    mov ah,al    
    in al,83h       ; then get page
    xchg ah,al
; ax is page + msb of addr (12 bits)
    shl ax,4        ; shift to upper 12 bits
    mov es,ax

; ES:DI = PHYS buffer addr

    or dsp_status,ADPCM_MODE
    ret
build_pc_ptr endp
;***************************************************************************
;
;***************************************************************************
dsp_4bit_adpcm_dac proc near
    call near ptr dsp_set_block_size        ; set upper and lower count size
IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_ADPCM_MARK
    call near ptr _add_to_debug_table
    call debug_dma
ENDIF
; Use previous ref byte as start point for this buffer ...
    call near ptr build_pc_ptr

;es:di = pointer to PC data
;app_dma_count = amount of data in PC to decompress
;app_adpcm_count = amount DSP was told to play ....

adpcm_common:
    mov app_dma_seg,es
    mov app_dma_off,di
    mov cx,SIZE_ADPCM                ; do a full buffer

    mov si,offset begin_adpcm

    and dsp_status,NOT ADPCM_BUFF2    ; next time to FIRST half of buffer

    call decomp_a_buffer    ; decompress some data ....

    mov app_dma_off,di        ; save where end of processed data is ...

; Now start up the DMA xfer
; if carry set, then this buffer is all the data there is ....
; if carry clear, then there's more. Set it up so we get IRQ at halfway pnt
    jnc more_data
; OK. Only a bit of data.... BX has the # of bytes to xfer ....
; Show its the last buffer
    or dsp_status,DONE_ADPCM
 ; so we stop after next irq ....
    and dsp_status,NOT (ADPCM_MODE+NEXT_2_LAST)
    jmp strt_xfer
more_data:
    mov bx,SIZE_ADPCM/2        ; halfway
    and dsp_status,NOT (NEXT_2_LAST)

strt_xfer:
    dec bx
    call near ptr prog_codec_play_ctr

; now set up PC's DMA addr to be my buffer ...
    mov cx,bx
    mov bx,offset begin_adpcm
    call near ptr prog_pcdma_adpcm_ctr

; Start 'er up !!!!
    call near ptr dsp_dac_dma_continue
    call near ptr change_from_pio
    ret
dsp_4bit_adpcm_dac endp


;************************************************************************
; ES:DI = apps compressed data buffer
; DS:SI = our decompress buffer
; CX = size of the buffer to decompress into
; app_adpcm_count = amount left of data to decompress before giving IRQ
;************************************************************************
decomp_a_buffer proc near
    xor ax,ax    ; clear 16 bit data word
    xor bx,bx    ; clear # bytes decompressed
    xor dx,dx    ; clear 16 bit data word
    shr cx,1        ; To get # of packed bytes in input buffer
    mov dl,adpcm_ref ; pre-load the reference byte

next_byte:
    mov al,es:[di]        ; got data byte, now decomp. it

; Basic algorithm.
; Add/subtract high nibble from reference byte to create new data byte. Then
; use the new byte as the reference byte and perform the same math on the
; low nibble. The new data byte becomes the reference for the next ADPCM
; byte.
    shr al,4              ; upper nibble first
    test al,08h           ; see if we are supposed to subtract it
    je short add_high
    xor al,8              ; nope its a subtract, clear sign bit
    shl ax,2
    sub dx,ax             ;subtract out the data
    cmp dh,0ffh
    jne short store_high  ; no under flow
    mov dx,0              ; set ref to bottom limit
    jmp short store_high

add_high:
    shl ax,2
    add dx,ax             ;add in the data
    cmp dh,1              ; did it roll over to high byte 
    jne store_high        ; no over flow
    mov dx,0ffh           ; set ref to top limit

store_high:
    mov ds:[si],dl        ; store it in the buffer

    mov al,es:[di]        ; got data byte, now decomp. it
    and al,0fh            ; now the low nibble

    test al,08h           ; see if we are supposed to subtract it
    je short add_low
    xor al,8              ; nope its a subtract, clear sign bit
    shl ax,2
    sub dx,ax             ;subtract out the data
    cmp dh,0ffh
    jne short store_low   ; no under flow
    mov dx,0              ; set ref to bottom limit
    jmp short store_low

add_low:
    shl ax,2
    add dx,ax             ;add in the data
    cmp dh,1
    jne store_low         ; no over flow
    mov dx,0ffh           ; set ref to top limit

store_low:
    mov adpcm_ref,dl      ; and save for next byte
    mov ds:[si+1],dl

    inc di                ; bump input buffer past this byte

    add si,2              ; bump output buffer to next byte
    inc bx                ; total up # of bytes that were decompressed
    dec app_dma_count     ; to put back proper cnt AFTER completion
    dec app_adpcm_count   ; keep track of # of bytes left to decompress 
                          ; from TOTAL amount app said to xfer
    je last_byte          ; jump if we are done with entire buffer
    loop next_byte        ; nope, try and put more data in SBOS buffer
; This SBOS buffer is now full. There may be more data from app to do
    clc
    ret

last_byte:
; bx = # of compressed bytes processed
    shl bx,1        ; make it the # of bytes added to decompressed buffer
    stc
    ret
decomp_a_buffer endp
;***************************************************************************
;
;***************************************************************************
dsp_4bit_adpcm_dac_ref proc near
    call near ptr dsp_set_block_size        ; set upper and lower count size
IFDEF D_SB
    mov dl,DMA_MARKER
    mov al,DMA_ADPCM_REF_MARK
    call near ptr _add_to_debug_table
    call debug_dma
ENDIF
; First byte in data is ref byte ....
; The rest is ADPCM data ....
    call near ptr build_pc_ptr
;es:di = pointer to PC data
;app_dma_count = amount of data in PC to decompress
;Remove first byte (reference byte) and adjust sizes
    mov al,es:[di]    ; get ref byte
    mov adpcm_ref,al
    inc di
    dec app_dma_count
    dec app_adpcm_count
    jmp adpcm_common        ; CAREFULL... Save a few bytes this way ...
dsp_4bit_adpcm_dac_ref endp

IFDEF NEVER
;***************************************************************************
;
;***************************************************************************
dsp_26bit_adpcm_dac proc near
    ret
dsp_26bit_adpcm_dac endp
;***************************************************************************
;
;***************************************************************************
dsp_26bit_adpcm_dac_ref proc near
    ret
dsp_26bit_adpcm_dac_ref endp
;***************************************************************************
;
;***************************************************************************
dsp_reset_buffer proc near
    ret
dsp_reset_buffer endp
ENDIF

IFDEF VAR_PCM
_asm_codec_freq proc near
; Enter w/ CX = frequency to playback
    mov dx,_reg_codec_addr
    mov al,CPVFI
    or  al,trd_bit
    out dx,al

    mov eax,1058400            ; 1690000/16
    xor edx,edx
    xor ebx,ebx
    mov bx,cx
    idiv ebx                  ; Divide it by the freq
; result is in ax
    sub ax,48
; al has value to program
    mov dx,_reg_codec_data
    out dx,al
    ret
_asm_codec_freq endp
ENDIF

change_to_pio proc near
; Gotta disable the fifo IRQ stuff or we're HUNG !!!!
    mov dx,_reg_codec_addr
    mov al,ALT_FEATURE_2
    or  al,trd_bit
    out dx,al
    inc dx
    in al,dx
    and al,NOT 0C0h      ; shut PPIE bit off
    out dx,al
    mov dx,_reg_codec_addr
    mov al,CODEC_MCE+IFACE_CTRL
    or  al,trd_bit
    out dx,al
    inc dx
    in al,dx
    or al,PLAYBACK_PIO+PLAYBACK_ENABLE+AUTOCALIB
    out dx,al
    mov dx,_reg_codec_addr
    mov al,IFACE_CTRL
    or  al,trd_bit
    out dx,al

    ret
change_to_pio endp

change_from_pio proc near

    mov dx,_reg_codec_addr
    mov al,CODEC_MCE+IFACE_CTRL
    or  al,trd_bit
    out dx,al
    inc dx
    in  al,dx
    and al,NOT PLAYBACK_PIO
    out dx,al
    mov dx,_reg_codec_addr
    mov al,IFACE_CTRL
    or  al,trd_bit
    out dx,al

; clear any pending IRQs
    mov    dx,_reg_codec_status
    in     al,dx

    mov    dx,_reg_codec_addr
    mov    al,IRQ_STATUS
    or     al,trd_bit
    out    dx,al
    inc    dx
    mov    al,CAPTURE_IRQ+TIMER_IRQ
    out    dx,al

    mov dx,_reg_codec_addr
    mov al,ALT_FEATURE_2
    or  al,trd_bit
    out dx,al
    inc    dx
    in al,dx

;This is test to see if we are auto-initing ona 2 byte buffer under Windows.
; If so, don't enable IRQ's. It will bog it down too much and it looks like
; apps don't care if they get the IRQ or not.....
    test   _shared,STAT_WINDOWS_RUNNING
    je     ppie_on
    test   dsp_status,AUTOINIT_MODE
    je     ppie_on
    cmp    _dsp_dma_size,1
    jne    ppie_on
    and    al,NOT 0C0h ; shut PPIE off ...
    jmp    ppie_prog

ppie_on:
    or al,0C0h      ; turn PPIE back on ....
ppie_prog:
    out dx,al
    ret
change_from_pio endp

_TEXT ends
    end
