;*********************************************************************
;
; Synth IRQ handler and related functions
;
;*********************************************************************
; THIS MODULE MUST ALL REMAIN RESIDENT
.386

INCLUDE syndefs.inc
INCLUDE sbosdefs.inc
INCLUDE dac.inc
INCLUDE dsp.inc

;SHOW_IRQS equ 1

_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 _new_intsynth
public _synth_handler

;---------------------------------------------------------------------
; LIST OF EXTERNS
;---------------------------------------------------------------------
extrn change_to_pio:near
extrn stop_timer:near
extrn handle_port_irq:near
extrn force_sb_irq:near
extrn dsp_dac_dma_continue:near
extrn dsp_prog_dac_block_size:near
extrn dsp_status:word
extrn _dsp_dma_size:word
extrn begin_adpcm:near
extrn half_adpcm:near
extrn decomp_a_buffer:near
extrn prog_dma_stuff:near
extrn simulate_sb_irq:near
extrn prog_pcdma_adpcm_ctr:near
extrn prog_codec_play_ctr:near
IFDEF DEBUG
extrn debug_dma:near
ENDIF
;
; "C" defined routines
;
extrn _shared:word
;extrn _proc_midi:near
extrn _proc_voice:near
extrn write_nmi:near
extrn endstack:near
;
; All needed SYNTH ports
;
extrn _reg_irq_status:word
extrn _reg_status_read:word
extrn _reg_reg_control:word
extrn _reg_index:word
extrn _reg_data_high:word
extrn _reg_data_low:word
extrn _reg_codec_addr:word
extrn _reg_codec_status:word
extrn _orig_irq1:byte
extrn _dsp_direct_mode:byte
extrn trd_bit:byte
extrn last_buff_cnt:word
extrn app_dma_seg:word
extrn app_dma_off:word
extrn app_dma_count:word

IFDEF DEBUG
extrn _add_to_debug_table:near
extrn changeborder:near
extrn debug_timer:near
ENDIF
save_irq    db      0
irq_ss      dw      ?
irq_sp      dw      ?

_DATA ends

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

    db    180 dup ('X')
top_irq_stk label near
public top_irq_stk

play_data proc near
    call   prog_codec_play_ctr
    mov    cx,bx
    mov    bx,di
    call   prog_pcdma_adpcm_ctr
    call   dsp_dac_dma_continue	; startup xfer
    ret
play_data endp

_synth_handler proc near
    call   near ptr write_nmi      ; write nmi vector to the vector table
    ;
    ; Check to see if the PORT traps were vectored to the synth IRQ
    ;
    mov    ax,word ptr _shared
    test   ax,STAT_COMBINED_IRQ
    je     short syn_loop
    call   near ptr handle_port_irq
    ;
    ; OK. Now handle the synth interrupts ....
    ;
syn_loop:
    mov    dx,_reg_irq_status
    in     al,dx
    cmp    al,0ffh
    je     done_all_irqs        ; forget these IRQ's .....!!!!

    and al,VALID_SYN_IRQS   ; check for wave,envelope
    cmp    al,00h                ; are there any pending IRQ's from the synth?
    je     short done_syn
more_syn:
    mov    save_irq,al         ; save for later checks

    test   save_irq,WAVE_IRQ+ENVEL_IRQ  ; check for wave or envelope IRQ
    je     short no_voice
    call   near ptr _proc_voice

no_voice:
    jmp    short syn_loop               ; keep going till all IRQs are serviced

done_syn:
    ;
    ; Need to service CODEC irqs here ....
    mov    ax,word ptr _shared
    test   ax,STAT_CODEC_ENABLED
    jne    short do_codec
    jmp    done_codec
    ;
    ; See if the codec has any IRQ's pending
    ;
do_codec:
    mov    dx,_reg_codec_status
    in     al,dx
    test   al,CODEC_INT
    je     done_codec        ; no more IRQ's pending

more_codec:

IFDEF D_SB
    mov    dl,DMA_MARKER
    mov    al,DMA_IRQ_MARK
    call   near ptr _add_to_debug_table
    call   near ptr debug_dma
ENDIF
    ; Now get IRQ cause(s) from codec
    mov    dx,_reg_codec_addr
    mov    al,IRQ_STATUS
    or     al,trd_bit
    out    dx,al
    inc    dx
    in     al,dx
    and    al,PLAYBACK_IRQ+CAPTURE_IRQ+TIMER_IRQ
    mov    save_irq,al

    test   save_irq,PLAYBACK_IRQ
    je     not_playback

    test   dsp_status,AUTOINIT_MODE
    jne    leave_pen_on

IFDEF NEVER
    mov    dl,DMA_MARKER
    mov    al,DMA_IRQ_MARK
    call   near ptr _add_to_debug_table
    call   near ptr debug_dma
ENDIF

    cmp    _dsp_direct_mode,1
    je     leave_pen_on

    test   dsp_status,NEXT_2_LAST
    je     not_finishing_up

    and    dsp_status, NOT (NEXT_2_LAST+ADPCM_MODE)
; now see if last piece of data is in first or sec buff
    test   dsp_status,ADPCM_BUFF2
    jne    finish_in_sec
; last part is in first buffer
    mov    di, offset begin_adpcm
    jmp    start_last
finish_in_sec:
    mov    di, offset half_adpcm
start_last:
; OK lets start the last partial buffer of data 
    mov    bx,last_buff_cnt
    dec    bx
    or     dsp_status,DONE_ADPCM
    call   near ptr play_data
    jmp    not_capture

not_finishing_up:

; Now check to see if we WERE JUST doing ADPCM stuff
    test   dsp_status,DONE_ADPCM
    je     short not_done_adpcm
    mov    al,5
    out    0ah,al    ; shut DMA down .....
    xor    dsp_status,DONE_ADPCM
; Now reset the PC's DMA counter so that it will be right if the app
; asks us to continue .... (Put them to what they would have been had
; SBOS not screwed around with them ....)
    mov    cx,app_dma_count
    mov    bx,app_dma_off
    mov    ax,app_dma_seg
; CAREFULL: This is to share some code ....
    call   prog_dma_stuff

not_done_adpcm:

    test   dsp_status,ADPCM_MODE
    jne    short leave_pen_on

;    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 ....
;    out    dx,al

; OK. There are up to 16 samples sitting in the fifo. If we disable playback
; here, the samples get dumped. So just change it to PIO and disable IRQs.
; That will play out the rest of the data and then stop ....

IFDEF D_SB
    mov dl,MARKER_CODE
    mov al,55h
    call near ptr _add_to_debug_table
ENDIF

    call change_to_pio

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

leave_pen_on:

    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

    cmp    _dsp_direct_mode,1
    je     not_capture                ; skip IRQ if in direct mode

; Now check to see if we are doing ADPCM stuff
    test   dsp_status,ADPCM_MODE
    je     short not_adpcm

; Ok this is an IRQ from an ADPCM buff, now what do I do ?
    test   dsp_status,ADPCM_BUFF2
    jne    short do_second_buff

    or     dsp_status,ADPCM_BUFF2     ; show sec buff needs to be filled on next IRQ
    mov    si,offset begin_adpcm
    mov    di,offset half_adpcm
    jmp    go_adpcm

do_second_buff:
    and    dsp_status,NOT ADPCM_BUFF2    ; next time to FIRST half of buffer
    mov    si,offset half_adpcm
    mov    di,offset begin_adpcm

go_adpcm:
; First start up DMA of next buffer (keep audio running)
    mov    bx,(SIZE_ADPCM/2)-1
    call   near ptr play_data

; 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
    mov    es,app_dma_seg
    mov    di,app_dma_off
    mov    cx,SIZE_ADPCM/2

    call   decomp_a_buffer
    mov    app_dma_off,di       ; save how far we got ....

; OK, now what do we do ???
    jnc    not_capture           ; still more data to do ....

; Ok. We are done with this decompression. lets finish 'er up....
    mov    last_buff_cnt,bx     ; save amount of data in last buff
    or     dsp_status,NEXT_2_LAST

    jmp    short not_capture

not_adpcm:
; always fake it if its in high speed mode ...
    test   dsp_status,HIGH_SPEED
    jne    short no_skip_irq
; OK. If its looping on 1 byte, skip IRQ gen (pg486)...
    test   dsp_status,AUTOINIT_MODE
    je     no_skip_irq
    cmp    _dsp_dma_size,1
    jne    no_skip_irq
    call   near ptr simulate_sb_irq
    jmp    short not_capture
no_skip_irq:

    call   near ptr force_sb_irq                ; notify the app that it happened
skip_irq:

    and    dsp_status,NOT HIGH_SPEED            ; shut it off just in case

    jmp    short not_capture                ; now check timer .....

not_playback:
    test   save_irq,CAPTURE_IRQ
    je     short not_capture

    test   dsp_status,AUTOINIT_MODE
    jne    short leave_cen_on

    mov    dx,_reg_codec_addr
    mov    al,IFACE_CTRL
    or     al,trd_bit
    out    dx,al
    inc    dx
    in     al,dx
    and    al,0FDH                ; CEN off ....
    out    dx,al

leave_cen_on:

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

    jmp    not_adpcm    ; re-use some code here ....

not_capture:
    test   save_irq,TIMER_IRQ
    je     short not_timer

IFDEF D_SB
    call changeborder
    mov    al,0
    mov    dl,TIMER_MARKER
    call   near ptr _add_to_debug_table
ENDIF
    call   near ptr stop_timer

    test   dsp_status,PULSE_SBIRQ
    je     short no_pulse
    xor    dsp_status,PULSE_SBIRQ        ; shut it down ....

; Now that the 'tick' has happend, let DMA happen ...
    mov    al,01h
    out    0ah,al        ; enable dma channel (just in case (mortal kombat?))...

; Start xfer that was postponed till later ....

; Look around here for SYNDICATE PLUS prob ....

    mov    dx,_reg_codec_addr
    mov    al,IFACE_CTRL
    or     al,trd_bit
    out    dx,al
    inc    dx
    in     al,dx
   or     al,PLAYBACK_ENABLE            ; enable playback 
;    or     al,CAPTURE_ENABLE             ; enable capture 
    out    dx,al
;    cmp    _dsp_dma_size,0
;    je     just_force_it
;    jmp    clear_timer_irq
no_pulse:
    test   dsp_status,SILENCE_DELAY
    je     short no_silence
    xor    dsp_status,SILENCE_DELAY        ; shut it down ....

just_force_it:
; force an int for silence delay ...
    call   near ptr force_sb_irq            ; time to FORCE an SB irq ...

no_silence:

clear_timer_irq:

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

not_timer:
    jmp    do_codec

done_codec:
; OK. It looks like both synth and CODEC are done. Now we check
; both again TWICE to make sure there are no pending irqs. If there
; are, then we MUST deal with them before leaving.

    mov    cx,2            ; check them both twice.
; OK. Do we have any irqs left ???
irq_chk:
    mov    dx,_reg_irq_status
    in     al,dx
    and    al,VALID_SYN_IRQS   ; check for wave,envelope
    cmp    al,00h                ; are there any pending IRQ's from the synth?
    je     short none_syn
    jmp    more_syn            ; OOPS. Got one left. Go back and get it.
none_syn:
; Is CODEC in system ??
    mov    ax,word ptr _shared
    test   ax,STAT_CODEC_ENABLED
    je     short no_codec                    ; all done ...

    mov    dx,_reg_codec_status
    in     al,dx
    test   al,CODEC_INT
    jne    more_codec        ; Got another CODEC irq. Deal with it.
no_codec:
    loop   irq_chk            ; Now check again.

done_all_irqs:
    ret
_synth_handler endp
;---------------------------------------------------------------------
; _new_intsynth()
;
; This is the new SYNTH IRQ handler
;---------------------------------------------------------------------
_new_intsynth proc far
    pushf
    cli
    push   ax
    ;
    ; Switch to local stack
    ; AX is pushed prior to this!!
    ;
    mov    cs:irq_ss,ss       ; save current ss
    mov    cs:irq_sp,sp       ; save current sp
    mov    ax,cs              ; load ss with cs
    mov    ss,ax
    lea    sp,top_irq_stk     ; load sp with end of local stack
    ; 
    ; Stack is setup - finish pushing regs
    ;
    pushad                 ; EAX-ECX-EDX-EBX-ESP-EBP-ESI-EDI
    push   ds
    push   es
    ;
    mov    ds,ax
IFDEF SHOW_IRQS
; blink the overscan to see how long it takes ....
    mov    dx,3daH
    in     al,dx
    mov    dx,3c0h
    mov    al,31h
    out    dx,al
    out    dx,al
ENDIF
    ;
    ; Clear the PIC(s)
    ;
    mov    al,EOI
    cmp    _orig_irq1,07h        ; Is it on the second PIC ?
    jle    short just_pic1
    mov    dx,PIC2                    ; Yes, send an EOI to it
    out    dx,al
just_pic1:
    jmp    short $+2
    mov    dx,PIC1                    ; Always send EOI to first PIC
    out    dx,al

    call   near ptr _synth_handler

    ;
    ; Restore reggies first ....
    ;
IFDEF SHOW_IRQS
; blink the overscan to see how long it takes ....
    mov    dx,3daH
    in     al,dx
    mov    dx,3c0h
    mov    al,31h
    out    dx,al
    mov    al,00h
    out    dx,al
ENDIF
    pop    es
    pop    ds
    popad
    ;
    ; Put stack back to apps.
    ;
    mov    ss,cs:irq_ss
    mov    sp,cs:irq_sp
    pop    ax
    popf
    iret
_new_intsynth endp

_TEXT ends
    end
