;=1.08c=;(modified for R2SR demo);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; FunkLite - By Super Real Darwin!                                         ;
;            Designed & Coded By Jason Nunn (JsNO)                         ;
;                                                                          ;
; The Playback routines..                                                  ;
;                                                                          ;
; Snail: 32 Rothdale Road, Moil, Darwin, NT, 0810, Australia               ;
; Email: root@superr.apana.org.au                                          ;
;      : jsno@turtle.apana.org.au                                          ;
;      : jsno@amigar.apana.org.au (no longer used)                         ;
; BBS  : ž Amiga Retreat BBS                                               ;
;          (089)451516  (3:850/105)                                        ;
;                                                                          ;
; =====================================================================    ;
;                                                                          ;
; Funktracker processes sound exclusively in backround. Unlike many        ;
; playback routines that require mixxer calls to be dotted through out     ;
; ones code, funktracker processes music totally in backround without any  ;
; foreground processing required.                                          ;
;                                                                          ;
; These is the attachment that contents all code necessary to play a FNK   ;
; module. Refer to the EXAMPLE.ASM for a demo of how to use.               ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Global                                                                   ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Subjective quality Scale (on my Pentium)
;
;              SHITHOUSE           GOOD               EXCELLENT
;              ³                   ³                  ³
;
;        SB2.0³*******
;        SBPRO³*********************
;          GUS³************************************
;         SB16³************************************************
;        PAS16³**********************************************
;
; ž PAS16 doesn't sound quite as good as the SB16

SB_CARD               = 0
SBPRO_CARD            = 1
GUS_VARB_CARD         = 2  ;<<---Channel panning can vary according to sample
SB15EM_CARD           = 3
SB16_CARD             = 4
GUS_FIXB_CARD         = 5  ;<<- fixed DAC/669 style left-right-left-right balances
RIPPED_CARD           = 6
PAS16_CARD            = 7

struc tinit_settings
  card_type           db ?
  PORT_no             dd ?
  IRQ_no              db ?
  DMA_no              db ?
  IRQ_no2             db ?
  DMA_no2             db ?
  DAC_Samplerate      db 4
ends
init_settings         tinit_settings <>

struc tDAC_ssetings            ; these struc variables control the DMA
  DAC_sr              dd ?     ; a sample rate for each card. The idea
  DAC_mix_buffer_size dd ?     ; is to maintain a 0.02 of a second
  DMA_length          dd ?     ; tick when processing DAC DMA transfers.
  DMA_real_sr         dd ?     ; both tracker and DAC mixxer run of the same interrupt.
ends

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Tracker Data                                                             ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Structures                               ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
STOP                   = 0
PLAY                   = 1

struc tfunk_sb
  sname                db 19 dup(?)
  start                dd ?
  length               dd ?
  volume               db ?    ; (0 to FF)
  balance              db ?    ; (0 to FF)
  pt_and_sop           db ?
  vv_waveform          db ?
  rl_and_as            db ?
ends

struc tfunk_hr
  sig                  dd ?
  info                 dd ?
  LZH_check_size       dd ?
  LZH_check_sum        dd ?    ;this field is no longer used for "checksum"
  loop_order           db ?    ; if FFh then no loop
  order_list           db 256 dup(?)
  break_list           db 128 dup(?)
  funk_sb              tfunk_sb 64 dup(<>)
ends

struc tfunk_chan
;Äcontrol systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  channel_kill         db PLAY
  command              db ?
  com_val              db ?
  comspd_count         db ?    ; crtl rapid speed delay
  sample               db ?    ; thenis serves no purpose
  start                dd ?
  length               dd ?
  funkctrl             db ?
  port_type            db ?
  sample_ofs_parm      db ?
  vib_waveform         db ?
  vol_vib_waveform     db ?
  retrig_spd_count     db ?
  retrig_count         db ?
  retrig_limit         db ?
  arp_speed            db ?
  balance              db ?    ; 0 to 15
;Änote systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  note_command         db ?
  note_com_val         db ?
  note_comspd_count    db ?    ; note rapid speed delay
  note                 db ?
  ifreq                dd ?    ; real_freq = FACTOR / invert_freq
  ifreq_vibrato        dd ?    ; used by vibrato, tremola, freqadetc
  ifreq_portdest       dd ?    ; used by porta
  rfreq                dd ?    ; the real frequency
  rfreq_adjust         dd ?
  rfreq_portdest       dd ?
  vib_ptr              db ?
  note_beat_count      db ?    ; used by arpeggio and retrig, fanin, fanout
;Ävolume systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  volume_command       db ?
  volume_com_val       db ?
  volume_comspd_count  db ?    ; volume rapid speed delay
  volume               db ?
  volume_vibrato       db ?
  volume_portdest      db ?
  rvolume              db ?
  vol_vib_ptr          db ?
  volume_beat_count    db ?    ; used by reverb, crest, though
;Äcard dependantÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  CARD_sample_ptr      dd ?
  CARD_freq            dd ?
  CARD_freq_attract    dd ?
  CARD_volume          dd ?
ends

struc tfunk_info
  trek_status          db ?
  sequence_ofs         db ?
  pattern_ofs          db ?
  sequence_ofs_old     db ?    ;for display purposes
  pattern_ofs2_old     db ?
  tempo                db ?
  tempo_count          db ?
  no_of_patterns       db ?
  no_of_sequences      db ?
  master_volume        db ?
  sample_ptrs          dd 64 dup(?)
  sample_block_size    dd ?
  funk_pd_size         db 80h
  sample_memory_lim    dd 00100000h
ends

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; var                                      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

note_table:
  dd 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680
  dd  7248, 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064
  dd  3840, 3624, 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152
  dd  2032, 1920, 1808, 1712, 1616, 1520, 1440, 1360, 1280, 1208, 1144
  dd  1080, 1016,  960,  904,  856,  808,  760,  720,  680,  640,  600
  dd   568,  536,  504,  480,  448,    0,    0,    0,    0
com_ifreq_inc:
  db  001,004,006,010,012,016,020,024
  db  031,047,063,083,107,139,187,251
com_rfreq_inc:
  dd  00016,00032,00048,00064,00096,00128,00160,00192
  dd  00244,00255,00383,00511,00639,00767,00895,01024
com_volume_inc:
  db  001,002,003,004,005,006,007,008
  db  010,012,016,020,024,032,048,064

com_sine_table:
  db   -6, -19, -31, -43, -55, -66, -76, -86
  db  -95,-103,-110,-116,-120,-124,-127,-128
  db -128,-127,-124,-121,-116,-110,-103, -95
  db  -86, -77, -66, -55, -44, -32, -19,  -7
  db    6,  18,  30,  42,  54,  65,  76,  85
  db   94, 102, 109, 115, 120, 124, 126, 127
  db  127, 126, 124, 121, 116, 110, 104,  96
  db   87,  77,  67,  56,  44,  32,  20,   8
com_triangle_table:
  db   -4, -12, -20, -28, -36, -44, -52, -60
  db  -68, -76, -84, -92,-100,-108,-116,-124
  db -124,-116,-108,-100, -92, -84, -76, -68
  db  -60, -52, -44, -36, -28, -20, -12,  -4
  db    4,  12,  20,  28,  36,  44,  52,  60
  db   68,  76,  84,  92, 100, 108, 116, 124
  db  123, 115, 107,  99,  91,  83,  75,  67
  db   59,  51,  43,  35,  27,  19,  11,   3
com_square_table:
  db -064,-128,-128,-127,-127,-128,-128,-127
  db -127,-128,-128,-127,-127,-128,-128,-127
  db -127,-128,-128,-127,-127,-128,-128,-127
  db -127,-128,-128,-127,-127,-128,-128,-064
  db  095, 127, 127, 126, 126, 127, 127, 126
  db  126, 127, 127, 126, 126, 127, 127, 126
  db  126, 127, 127, 126, 126, 127, 127, 126
  db  126, 127, 127, 126, 126, 127, 127, 095
com_sawtooth_table:
  db   -1,  -5,  -9, -13, -17, -21, -25, -29
  db  -33, -37, -41, -45, -49, -53, -57, -61
  db  -65, -69, -73, -77, -81, -85, -89, -93
  db  -97,-101,-105,-109,-113,-117,-121,-125
  db  127, 123, 119, 115, 111, 107, 103,  99
  db   95,  91,  87,  83,  79,  75,  71,  67
  db   63,  59,  55,  51,  47,  43,  39,  35
  db   31,  27,  23,  19,  15,  11,   7,   3
com_random_table:
  db  168, 167, 167, 165, 201, 201, 202, 201
  db  244, 244, 244, 244,  85,  82,  85,  85
  db   44,  44,  44,  44, 114, 114, 114, 114
  db   77,  77,  73,  77, 116, 116, 116, 116
  db   87,  87,  87,  87,  31,  31,  31,  31
  db  198, 198, 198, 198, 105, 104, 105, 105
  db  103, 105, 103, 103, 166, 166, 165, 166
  db  171, 172, 171, 171, 217, 217, 217, 217

funk_info             tfunk_info <>
funk_chan1            tfunk_chan <>
funk_chan2            tfunk_chan <>
funk_chan3            tfunk_chan <>
funk_chan4            tfunk_chan <>
funk_chan5            tfunk_chan <>
funk_chan6            tfunk_chan <>
funk_chan7            tfunk_chan <>
funk_chan8            tfunk_chan <>

funk_chan9            tfunk_chan <>  ;used for effects
funk_chanA            tfunk_chan <>
funk_chanB            tfunk_chan <>
funk_chanC            tfunk_chan <>

CARD_freq_convert     dd ?
CARD_volume_convert   dd ?

funk_hr_ptr           dd ?
funk_sd_ptr           dd ?

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  GET   IRQ vector             ;
;  expects  BL = IRQ (0..15)    ;
;  returns  CX = selector       ;
;           EDX = offset        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc getirqvector
                push    eax ebx
                and     bl,0fh
                cmp     bl,8
                jb      @@J2
                add     bl,60h
@@J2:           add     bl,8h
                mov     ax,204h
                int     31h
                pop     ebx eax
                ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;SET   IRQ vector               ;
;  expects  BL = IRQ (0..15)    ;
;           CX = selector       ;
;           EDX = offset        ;
;  returns Nothing              ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc setirqvector
                push    eax ebx
                and     bl,0fh
                cmp     bl,8
                jb      @@J1
                add     bl,60h
@@J1:           add     bl,8h
                mov     ax,205h
                int     31h
                pop     ebx eax
                ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Tracker Code                                                             ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
; ebx = ifreq, returns = eax       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc ifreq_to_rfreq_vib
  add    ebx,[dword edi+tfunk_chan.ifreq_vibrato]
  js     @@zero
  jz     @@zero
  jmp    @@set_r
@@zero:
  mov    ebx,1
@@set_r:
proc ifreq_to_rfreq
  xor    edx,edx
  mov    eax,1b4f4d0h
  div    ebx
  add    eax,[dword edi+tfunk_chan.rfreq_adjust]  ; add 669 freq adjustment
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc vol_to_realvol
  mov    bl,[byte edi+tfunk_chan.volume]
  mov    [byte edi+tfunk_chan.rvolume],bl
  ret
endp

proc vol_to_realvol_vib
  movzx  ebx,[byte edi+tfunk_chan.volume]
  add    ebx,1000
  movzx  eax,[byte edi+tfunk_chan.volume_vibrato]
  or     al,al
  js     @@sub
  add    ebx,eax
  cmp    ebx,1000+0ffh
  ja     @@adjust_add
  jmp    @@done
@@adjust_add:
  mov    ebx,1000+0ffh
  jmp    @@done
@@sub:
  neg    al
  sub    ebx,eax
  cmp    ebx,1000
  jb     @@adjust_sub
  jmp    @@done
@@adjust_sub:
  mov    ebx,1000
@@done:
  sub    ebx,1000
  mov    [byte edi+tfunk_chan.rvolume],bl
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
; bl = note, returns bx = ifreq    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
macro @note_2_ifreq
  and    ebx,111111b
  lea    ebx,[ebx*4]
  mov    ebx,[dword ebx+note_table]
endm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
; misc. vibrato function used by   ;
; frequency and volume functions   ;
;                                  ;
; al = waveform                    ;
; ah = amplitude                   ;
; cl = speed                       ;
; ebx = ptr to vibrato table       ;
; returns al = value, ebx = ptr    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vib_func
  inc    ah
  cmp    al,1
  je     @@vib_triangle
  cmp    al,2
  je     @@vib_square
  cmp    al,3
  je     @@vib_sawtooth
  cmp    al,4
  je     @@vib_random
@@vib_sine:
  mov    al,[byte ebx+com_sine_table]
  jmp    @@get_func_value
@@vib_triangle:
  mov    al,[byte ebx+com_triangle_table]
  jmp    @@get_func_value
@@vib_square:
  mov    al,[byte ebx+com_square_table]
  jmp    @@get_func_value
@@vib_sawtooth:
  mov    al,[byte ebx+com_sawtooth_table]
  jmp    @@get_func_value
@@vib_random:
  mov    al,[byte ebx+com_random_table]
@@get_func_value:
  imul   ah
  shr    eax,4
  not    cl
  and    cl,1111b
  add    bl,cl
  inc    bl
  and    bl,111111b
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
; for NOTE 3D: reload samples      ;
; attributes                       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc reload_sample_attr
  movzx  eax,[word esi]                            ;extract sample no
  xchg   al,ah
  shr    eax,4
  and    eax,03fh
  mov    [byte edi+tfunk_chan.sample],al
  shl    eax,5
  add    eax,[funk_hr_ptr]
  add    eax,offset (tfunk_hr).funk_sb
proc reload_sample2
  mov    bl,[byte eax+tfunk_sb.balance]           ; get balance
  mov    [byte edi+tfunk_chan.balance],bl
  mov    bl,[byte eax+tfunk_sb.pt_and_sop]        ; get both port type and sample ofs parm
  mov    dl,bl
  shr    bl,4
  and    dl,1111b
  mov    [byte edi+tfunk_chan.port_type],bl
  mov    [byte edi+tfunk_chan.sample_ofs_parm],dl
  mov    bl,[byte eax+tfunk_sb.vv_waveform]       ; get both vibrato wf & tremola wf
  mov    dl,bl
  shr    bl,4
  and    dl,1111b
  mov    [byte edi+tfunk_chan.vib_waveform],bl
  mov    [byte edi+tfunk_chan.vol_vib_waveform],dl
  mov    bl,[byte eax+tfunk_sb.rl_and_as]         ; get both retrig speed & arp speed
  mov    dl,bl
  shr    bl,4
  and    dl,1111b
  mov    [byte edi+tfunk_chan.retrig_limit],bl
  mov    [byte edi+tfunk_chan.arp_speed],dl
  mov    dl,[byte eax+tfunk_sb.volume]
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc decode_sample
  movzx  eax,[word esi]                            ;extract sample no
  xchg   al,ah
  shr    eax,4
  and    eax,03fh
  mov    [byte edi+tfunk_chan.sample],al
proc do_retrig_sample
  movzx  eax,al
  push   eax
  shl    eax,5
  add    eax,[funk_hr_ptr]
  add    eax,offset (tfunk_hr).funk_sb
  mov    [byte edi+tfunk_chan.funkctrl],10b       ; set funkctrl
  mov    ebx,[dword eax+tfunk_sb.start]
  cmp    ebx,0ffffffffh
  je     @@dont_loop
  mov    [byte edi+tfunk_chan.funkctrl],11b       ; set funkctrl
@@dont_loop:
  mov    [dword edi+tfunk_chan.start],ebx
  mov    ebx,[dword eax+tfunk_sb.length]
  mov    [dword edi+tfunk_chan.length],ebx
  call   reload_sample2
  pop    eax
  lea    eax,[eax*4]
  mov    eax,[dword eax+funk_info.sample_ptrs]
  add    [dword edi+tfunk_chan.start],eax
  add    [dword edi+tfunk_chan.length],eax
  mov    [dword edi+tfunk_chan.CARD_sample_ptr],eax
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc do_full_note
  mov    [byte edi+tfunk_chan.note],bl
  mov    bl,[byte edi+tfunk_chan.note]
  mov    [byte edi+tfunk_chan.volume],dl
  @note_2_ifreq
  mov    [dword edi+tfunk_chan.ifreq],ebx
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   vol_to_realvol
  call   [dword CARD_freq_convert]
  call   [dword CARD_volume_convert]
  ret
endp

proc load_attr_active
  mov    [byte edi+tfunk_chan.volume],dl
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

proc normal_decode_system
  mov    bl,[byte esi]                            ;extract note value
  shr    bl,2
  cmp    bl,3dh                                   ; of ReLOad samples then ...
  je     @@ReLOad_samples
  push   ebx
  call   decode_sample
  pop    ebx
  cmp    bl,3eh                                   ; of SAMPLE ONLY slot
  je     @@no_note_change                         ; then dont change note
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [byte edi+tfunk_chan.volume_command],0fh
  call   do_full_note
@@no_note_change:
  ret
@@ReLOad_samples:
  call   reload_sample_attr
  call   load_attr_active
  ret
endp

proc normal_decode_note
  mov    bl,[byte esi]                            ;extract note value
  shr    bl,2
  cmp    bl,3dh                                   ; of ReLOad samples then ...
  je     @@ReLOad_samples
  push   ebx
  call   decode_sample
  pop    ebx
  cmp    bl,3eh                                   ; of SAMPLE ONLY slot
  je     @@no_note_change                         ; then dont change note
  mov    [byte edi+tfunk_chan.volume_command],0fh
  call   do_full_note
@@no_note_change:
  ret
@@ReLOad_samples:
  call   reload_sample_attr
  call   load_attr_active
  ret
endp

proc normal_decode_volume
  mov    bl,[byte esi]                            ;extract note value
  shr    bl,2
  cmp    bl,3dh                                   ; of ReLOad samples then ...
  je     @@ReLOad_samples
  push   ebx
  call   decode_sample
  pop    ebx
  cmp    bl,3eh                                   ; of SAMPLE ONLY slot
  je     @@no_note_change                         ; then dont change note
  mov    [byte edi+tfunk_chan.note_command],0fh
  call   do_full_note
@@no_note_change:
  ret
@@ReLOad_samples:
  call   reload_sample_attr
  call   load_attr_active
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc comc_decode
  mov    bl,[byte esi]                            ;extract note value
  shr    bl,2
  cmp    bl,3dh                                   ; of ReLOad samples then ...
  je     @@ReLOad_samples
  push   ebx
  call   decode_sample
  pop    ebx
  cmp    bl,3eh                                   ; of SAMPLE ONLY slot
  je     @@cc_no_note_change                      ; then dont change note
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],dl
  mov    [byte edi+tfunk_chan.note],bl
  @note_2_ifreq
  mov    [dword edi+tfunk_chan.ifreq_portdest],ebx
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq_portdest],eax
  call   vol_to_realvol
  call   [dword CARD_freq_convert]
  call   [dword CARD_volume_convert]
@@cc_no_note_change:
  ret
@@ReLOad_samples:
  call   reload_sample_attr
  call   load_attr_active
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc comi_decode
  mov    bl,[byte esi]                            ;extract note value
  shr    bl,2
  cmp    bl,3dh                                   ; of ReLOad samples then ...
  je     @@ReLOad_samples
  push   ebx
  call   decode_sample
  pop    ebx
  cmp    bl,3eh                                   ; of SAMPLE ONLY slot
  je     @@no_note_change                         ; then dont change note
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [byte edi+tfunk_chan.volume_portdest],dl
  mov    [byte edi+tfunk_chan.note],bl
  @note_2_ifreq
  mov    [dword edi+tfunk_chan.ifreq],ebx
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   vol_to_realvol
  call   [dword CARD_freq_convert]
  call   [dword CARD_volume_convert]
  ret
@@no_note_change:
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [byte edi+tfunk_chan.volume_portdest],dl
  call   vol_to_realvol
  call   [dword CARD_freq_convert]
  call   [dword CARD_volume_convert]
  ret
@@ReLOad_samples:
  call   reload_sample_attr
  jmp    @@no_note_change
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;0Com A: port up                   ;
;                                  ;
; com_val : 0000   0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_port_up_log
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  and    ebx,1111b
  movzx  eax,[byte ebx+com_ifreq_inc]
  sub    [dword edi+tfunk_chan.ifreq],eax
  cmp    [dword edi+tfunk_chan.ifreq],300
  ja     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.ifreq],300
@@done:
  mov    ebx,[dword edi+tfunk_chan.ifreq]
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   [dword CARD_freq_convert]
  ret
endp

proc com_port_up_lin
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  and    ebx,1111b
  lea    ebx,[ebx*4]
  mov    eax,[dword ebx+com_rfreq_inc]
  add    [dword edi+tfunk_chan.rfreq],eax
  cmp    [dword edi+tfunk_chan.rfreq],174deh
  jb     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.rfreq],174deh
@@done:
  call   [dword CARD_freq_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;1Com B: port down                 ;
;                                  ;
; com_val : 0000   0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_port_dn_log
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  and    ebx,1111b
  movzx  eax,[byte ebx+com_ifreq_inc]
  add    [dword edi+tfunk_chan.ifreq],eax
  cmp    [dword edi+tfunk_chan.ifreq],20000
  jb     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.ifreq],20000
@@done:
  mov    ebx,[dword edi+tfunk_chan.ifreq]
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   [dword CARD_freq_convert]
  ret
endp

proc com_port_dn_lin
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  and    ebx,1111b
  lea    ebx,[ebx*4]
  mov    eax,[dword ebx+com_rfreq_inc]
  sub    [dword edi+tfunk_chan.rfreq],eax
  cmp    [dword edi+tfunk_chan.rfreq],1431
  ja     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.rfreq],1431
@@done:
  call   [dword CARD_freq_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;2Com C: port to note              ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_porta_log
  mov    eax,[dword edi+tfunk_chan.ifreq_portdest]
  cmp    [dword edi+tfunk_chan.ifreq],eax
  jne    @@not_same
  mov    [byte edi+tfunk_chan.note_command],0fh
  ret
@@not_same:
  ja     @@do_portup
  call   com_port_dn_log
  mov    eax,[dword edi+tfunk_chan.ifreq_portdest]
  cmp    [dword edi+tfunk_chan.ifreq],eax
  jb     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.ifreq],eax
  jmp    @@done
@@do_portup:
  call   com_port_up_log
  mov    eax,[dword edi+tfunk_chan.ifreq_portdest]
  cmp    [dword edi+tfunk_chan.ifreq],eax
  ja     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.ifreq],eax
@@done:
  mov    ebx,[dword edi+tfunk_chan.ifreq]
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   [dword CARD_freq_convert]
  ret
endp

proc com_porta_lin
  mov    eax,[dword edi+tfunk_chan.rfreq_portdest]
  cmp    [dword edi+tfunk_chan.rfreq],eax
  jne    @@not_same
  mov    [byte edi+tfunk_chan.note_command],0fh
  ret
@@not_same:
  jb     @@do_portup
  call   com_port_dn_lin
  mov    eax,[dword edi+tfunk_chan.rfreq_portdest]
  cmp    [dword edi+tfunk_chan.rfreq],eax
  ja     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.rfreq],eax
  jmp    @@done
@@do_portup:
  call   com_port_up_lin
  mov    eax,[dword edi+tfunk_chan.rfreq_portdest]
  cmp    [dword edi+tfunk_chan.rfreq],eax
  jb     @@done
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [dword edi+tfunk_chan.rfreq],eax
@@done:
  call   [dword CARD_freq_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc note_system_rapid_p1
  mov    eax,[dword edi+tfunk_chan.note_command]
  cmp    al,02h
  jbe    @@do_rp1
  ret
@@do_rp1:
  shr    ah,4
  cmp    [byte edi+tfunk_chan.note_comspd_count],ah
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.note_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  cmp    [byte edi+tfunk_chan.port_type],1
  je     @@line_porta
  or     al,al
  je     @@command_a_i
  cmp    al,01h
  je     @@command_b_i
  cmp    al,02h
  je     @@command_c_i
  ret
@@line_porta:
  or     al,al
  je     @@command_a_r
  cmp    al,01h
  je     @@command_b_r
  cmp    al,02h
  je     @@command_c_r
  ret
@@command_a_i:
  call   com_port_up_log
  ret
@@command_a_r:
  call   com_port_up_lin
  ret
@@command_b_i:
  call   com_port_dn_log
  ret
@@command_b_r:
  call   com_port_dn_lin
  ret
@@command_c_i:
  call   com_porta_log
  ret
@@command_c_r:
  call   com_porta_lin
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;3Com D: vibrato                   ;
;                                  ;
; com_val :   0000   0000          ;
;           \speed/ \amplitude/    ;
;                                  ;
;vib_waveform = 0   : sine         ;
;               1   : triangle     ;
;               2   : square       ;
;               3   : sawtooth     ;
;               4   : random       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vibrato
  mov    ah,[byte edi+tfunk_chan.note_com_val]
  mov    cl,ah
  and    ah,00001111b
  shr    cl,4
proc com_vibrato_minor
  movzx  ebx,[byte edi+tfunk_chan.vib_ptr]
  mov    al,[byte edi+tfunk_chan.vib_waveform]
  call   com_vib_func
  mov    [byte edi+tfunk_chan.vib_ptr],bl
  movsx  eax,al
  shl    eax,1
  mov    [dword edi+tfunk_chan.ifreq_vibrato],eax
  mov    ebx,[dword edi+tfunk_chan.ifreq]
  call   ifreq_to_rfreq_vib
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   [dword CARD_freq_convert]
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;4Com E: vibrato Fanin             ;
;                                  ;
; com_val :   0000   0000          ;
;           \speed/ \fanin value/  ;
;                                  ;
;vib_waveform = 0   : sine         ;
;               1   : triangle     ;
;               2   : square       ;
;               3   : sawtooth     ;
;               4   : random       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vib_fanin
  mov    ah,[byte edi+tfunk_chan.note_com_val]
  mov    cl,ah
  and    ah,00001111b
  and    cl,11110000b
  shr    cl,2
  cmp    [byte edi+tfunk_chan.note_beat_count],0fh
  jae    @@do_vibrato
  cmp    [byte edi+tfunk_chan.note_comspd_count],cl
  jae    @@inc_fan
  inc    [byte edi+tfunk_chan.note_comspd_count]
  jmp    @@do_vibrato
@@inc_fan:
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  inc    [byte edi+tfunk_chan.note_beat_count]
@@do_vibrato:
  mov    cl,[byte edi+tfunk_chan.note_beat_count]
  call   com_vibrato_minor
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;5Com F: vibrato Fanout            ;
;                                  ;
; com_val :   0000   0000          ;
;           \speed/ \fanin value/  ;
;                                  ;
;vib_waveform = 0   : sine         ;
;               1   : triangle     ;
;               2   : square       ;
;               3   : sawtooth     ;
;               4   : random       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vib_fanout
  mov    ah,[byte edi+tfunk_chan.note_com_val]
  mov    cl,ah
  and    ah,00001111b
  and    cl,11110000b
  shr    cl,2
  cmp    [byte edi+tfunk_chan.note_beat_count],0
  je     @@do_vibrato
  cmp    [byte edi+tfunk_chan.note_comspd_count],cl
  jae    @@dec_fan
  inc    [byte edi+tfunk_chan.note_comspd_count]
  jmp    @@do_vibrato
@@dec_fan:
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  dec    [byte edi+tfunk_chan.note_beat_count]
@@do_vibrato:
  mov    cl,[byte edi+tfunk_chan.note_beat_count]
  call   com_vibrato_minor
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;6Com G: volume sld up             ;
;                                  ;
; com_val : 0000   0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vol_up
  mov    bl,[byte edi+tfunk_chan.volume_com_val]
  and    ebx,1111b
  mov    al,[byte ebx+com_volume_inc]
  mov    ah,[byte edi+tfunk_chan.volume]
  add    ah,al
  jnc    @@adjust
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],255
  jmp    @@done
@@adjust:
  mov    [byte edi+tfunk_chan.volume],ah
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;7Com H: volume slide down         ;
;                                  ;
; com_val : 0000   0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vol_dn
  mov    bl,[byte edi+tfunk_chan.volume_com_val]
  and    ebx,1111b
  mov    al,[byte ebx+com_volume_inc]
  mov    ah,[byte edi+tfunk_chan.volume]
  sub    ah,al
  jnc    @@adjust
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],0
  jmp    @@done
@@adjust:
  mov    [byte edi+tfunk_chan.volume],ah
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;8Com I: volume porta (Rapid ctrl) ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vol_porta
  mov    al,[byte edi+tfunk_chan.volume_portdest]
  cmp    [byte edi+tfunk_chan.volume],al
  jne    @@not_same
  mov    [byte edi+tfunk_chan.volume_command],0fh
  jmp    @@done
@@not_same:
  jb     @@do_portup
  call   com_vol_dn
  mov    al,[byte edi+tfunk_chan.volume_portdest]
  cmp    [byte edi+tfunk_chan.volume],al
  ja     @@done
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],al
  jmp    @@done
@@do_portup:
  call   com_vol_up
  mov    al,[byte edi+tfunk_chan.volume_portdest]
  cmp    [byte edi+tfunk_chan.volume],al
  jb     @@done
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],al
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc volume_system_rapid_p1
  mov    eax,[dword edi+tfunk_chan.volume_command]
  cmp    al,06h
  jae    @@do_rp1
  ret
@@do_rp1:
  cmp    al,08h
  jbe    @@do_rp2
  ret
@@do_rp2:
  shr    ah,4
  cmp    [byte edi+tfunk_chan.volume_comspd_count],ah
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.volume_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  cmp    al,06h
  je     @@command_g
  cmp    al,07h
  je     @@command_h
  cmp    al,08h
  je     @@command_i
  ret
@@command_g:
  call   com_vol_up
  ret
@@command_h:
  call   com_vol_dn
  ret
@@command_i:
  call   com_vol_porta
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;9Com J: Volume Reverb             ;
;                                  ;
; A simulated echo effect          ;
;                                  ;
; com_val : 0000   0000            ;
;                                  ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_reverb
  mov    cl,[byte edi+tfunk_chan.volume_com_val]
  mov    ch,cl
  mov    dl,cl
  and    ch,00001111b
  and    cl,11110000b
  shr    cl,2
  cmp    [byte edi+tfunk_chan.volume_comspd_count],cl
  jae    @@echo
  inc    [byte edi+tfunk_chan.volume_comspd_count]
@@do_slide:
  cmp    [byte edi+tfunk_chan.volume],0
  je     @@done
  movzx  ecx,ch
  mov    al,[byte ecx+com_volume_inc]
  mov    ah,[byte edi+tfunk_chan.volume]
  sub    ah,al
  jnc    @@adjust
  mov    [byte edi+tfunk_chan.volume],0
  jmp    @@done
@@adjust:
  mov    [byte edi+tfunk_chan.volume],ah
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
@@echo:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  neg    dl
  shr    dl,4
  sub    [byte edi+tfunk_chan.volume_beat_count],dl
  jc     @@end
  mov    al,[byte edi+tfunk_chan.volume_beat_count]
  mov    [byte edi+tfunk_chan.volume],al
  jmp    @@done
@@end:
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],0
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ACom K: tremola                   ;
;                                  ;
; com_val :  0000    0000          ;
;                   \rate/         ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_tremola
  mov    ah,[byte edi+tfunk_chan.volume_com_val]
  mov    cl,ah
  and    ah,00001111b
  shr    cl,4
  movzx  ebx,[byte edi+tfunk_chan.vol_vib_ptr]
  mov    al,[byte edi+tfunk_chan.vol_vib_waveform]
  call   com_vib_func
  mov    [byte edi+tfunk_chan.vol_vib_ptr],bl
  mov    [byte edi+tfunk_chan.volume_vibrato],al
  call   vol_to_realvol_vib
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;BCom L: arpeggio                  ;
;                                  ;
; com_val :  0000    0000          ;
;            \N1/    \N2/          ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_arpeggio
  mov    al,[byte edi+tfunk_chan.note_com_val]
  mov    bh,[byte edi+tfunk_chan.arp_speed]
  cmp    [byte edi+tfunk_chan.note_comspd_count],bh
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.note_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  mov    bl,[byte edi+tfunk_chan.note]
  cmp    [byte edi+tfunk_chan.note_beat_count],1   ; arp_type 1
  je     @@arp1_t1
  cmp    [byte edi+tfunk_chan.note_beat_count],2
  je     @@arp1_t2
  jmp    @@done
@@arp1_t1:
  shr    al,4
  jmp    @@arp_it
@@arp1_t2:
  and    al,1111b
@@arp_it:
  add    bl,al
  cmp    bl,59
  jbe    @@done
  mov    bl,[byte edi+tfunk_chan.note]
@@done:
  @note_2_ifreq
  mov    [dword edi+tfunk_chan.ifreq],ebx
  call   ifreq_to_rfreq
  mov    [dword edi+tfunk_chan.rfreq],eax
  call   [dword CARD_freq_convert]
  cmp    [byte edi+tfunk_chan.note_beat_count],2
  je     @@clr_b
  inc    [byte edi+tfunk_chan.note_beat_count]
  ret
@@clr_b:
  mov    [byte edi+tfunk_chan.note_beat_count],0
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;CCom M: sample offset             ;
;                                  ;
; This is a all present sample     ;
; offset that is effective all the ;
; time for a given channel         ;
;                                  ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_sample_offset
  movzx  eax,[byte edi+tfunk_chan.com_val]
  mov    cl,[byte edi+tfunk_chan.sample_ofs_parm]
  shl    eax,cl
  add    [dword edi+tfunk_chan.CARD_sample_ptr],eax
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;DCom N: volume                    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_volume
  mov    al,[byte edi+tfunk_chan.volume_com_val]
  mov    [byte edi+tfunk_chan.volume],al
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;ECom O0: misc control             ;
;                                  ;
;value  control set                ;
;------------------                ;
;0     vibrato sine                ;
;1     vibrato triangle            ;
;2     vibrato square              ;
;3     vibrato sawtooth            ;
;4     vibrato random              ;
;5     tremola sine                ;
;6     tremola triangle            ;
;7     tremola square              ;
;8     tremola sawtooth            ;
;9     tremola random              ;
;a     halt note system            ;
;b     halt volume system          ;
;c     halt all systems            ;
;d     invert funkcrtl             ;
;e     algorithmic porting         ;
;f     linear porting              ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_misccrtl
  mov    al,[byte edi+tfunk_chan.com_val]
  and    al,00001111b
  or     al,al
  jz     @@vib_sine
  cmp    al,01h
  je     @@vib_triangle
  cmp    al,02h
  je     @@vib_square
  cmp    al,03h
  je     @@vib_sawtooth
  cmp    al,04h
  je     @@vib_random
  cmp    al,05h
  je     @@trm_sine
  cmp    al,06h
  je     @@trm_triangle
  cmp    al,07h
  je     @@trm_square
  cmp    al,08h
  je     @@trm_sawtooth
  cmp    al,09h
  je     @@trm_random
  cmp    al,0ah
  je     @@halt_note_sys
  cmp    al,0bh
  je     @@halt_vol_sys
  cmp    al,0ch
  je     @@halt_all_sys
  cmp    al,0dh
  je     @@invert_funk
  cmp    al,0eh
  je     @@log_porta
  cmp    al,0fh
  je     @@lin_porta
  ret
@@vib_sine:
  mov    [byte edi+tfunk_chan.vib_waveform],0
  ret
@@vib_triangle:
  mov    [byte edi+tfunk_chan.vib_waveform],1
  ret
@@vib_square:
  mov    [byte edi+tfunk_chan.vib_waveform],2
  ret
@@vib_sawtooth:
  mov    [byte edi+tfunk_chan.vib_waveform],3
 ret
@@vib_random:
  mov    [byte edi+tfunk_chan.vib_waveform],4
  ret
@@trm_sine:
  mov    [byte edi+tfunk_chan.vol_vib_waveform],0
  ret
@@trm_triangle:
  mov    [byte edi+tfunk_chan.vol_vib_waveform],1
  ret
@@trm_square:
  mov    [byte edi+tfunk_chan.vol_vib_waveform],2
  ret
@@trm_sawtooth:
  mov    [byte edi+tfunk_chan.vol_vib_waveform],3
  ret
@@trm_random:
  mov    [byte edi+tfunk_chan.vol_vib_waveform],4
  ret
@@halt_note_sys:
  mov    [byte edi+tfunk_chan.note_command],0fh
  ret
@@halt_vol_sys:
  mov    [byte edi+tfunk_chan.volume_command],0fh
  ret
@@halt_all_sys:
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [byte edi+tfunk_chan.volume_command],0fh
  ret
@@invert_funk:
  xor    [byte edi+tfunk_chan.funkctrl],1b
  mov    [byte edi+tfunk_chan.start],0
  ret
@@log_porta:
  mov    [byte edi+tfunk_chan.port_type],0
  ret
@@lin_porta:
  mov    [byte edi+tfunk_chan.port_type],1
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O1: volume cut               ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_volume_cut
  mov    al,[byte edi+tfunk_chan.volume_com_val]
  and    al,1111b
  cmp    [byte edi+tfunk_chan.volume_comspd_count],al
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.volume_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],0
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O2: real frequency adjust    ;
;                                  ;
; - this adjustment is independant ;
; and is post added                ;
; - this is a real frequency       ;
; adjustment                       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_real_freqad
  mov    al,[byte edi+tfunk_chan.com_val]
  and    eax,1111b
  shl    eax,3
  mov    [dword edi+tfunk_chan.rfreq_adjust],eax
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O3: set arpeggio speed       ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_arp_speed_set
  mov    al,[byte edi+tfunk_chan.com_val]
  and    al,1111b
  mov    [byte edi+tfunk_chan.arp_speed],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O4: fine port_up             ;
;                                  ;
; com_val :        0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_fine_port_up
  cmp    [byte edi+tfunk_chan.port_type],1
  je     @@line_porta
  call   com_port_up_log
  ret
@@line_porta:
  call   com_port_up_lin
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O5: fine port_dn             ;
;                                  ;
; com_val :        0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_fine_port_dn
  cmp    [byte edi+tfunk_chan.port_type],1
  je     @@line_porta
  call   com_port_dn_log
  ret
@@line_porta:
  call   com_port_dn_lin
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O6: fine vol sld up          ;
;                                  ;
; com_val :        0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O7: fine vol sld dn          ;
;                                  ;
; com_val :        0000            ;
;                 \rate/           ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O8: volume crest             ;
;                                  ;
; com_val :        0000            ;
;                 \speed/ (prehand);
;                                  ;
; volume slides up then down,      ;
; starting at current frequency    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vol_crest
  mov    bl,[byte edi+tfunk_chan.volume_com_val]
  and    ebx,1111b
  mov    dl,[byte ebx+com_volume_inc]
  cmp    [byte edi+tfunk_chan.volume_beat_count],1
  je     @@wait_a_bit
  cmp    [byte edi+tfunk_chan.volume_beat_count],2
  je     @@slide_down
  add    [byte edi+tfunk_chan.volume],dl
  jc     @@second_phase
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
@@second_phase:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  inc    [byte edi+tfunk_chan.volume_beat_count]
  mov    [byte edi+tfunk_chan.volume],0ffh
  jmp    @@done
@@wait_a_bit:
  inc    bl
  neg    bl
  and    bl,1111b
  cmp    [byte edi+tfunk_chan.volume_comspd_count],bl
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.volume_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  inc    [byte edi+tfunk_chan.volume_beat_count]
  ret
@@slide_down:
  shr    dl,2
  inc    dl
  mov    al,[byte edi+tfunk_chan.volume_portdest]
  sub    [byte edi+tfunk_chan.volume],dl
  jc     @@end
  cmp    [byte edi+tfunk_chan.volume],al
  ja     @@done
@@end:
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],al
  jmp    @@done
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com O9: volume trough            ;
;                                  ;
; com_val :        0000            ;
;                 \speed/ (prehand);
;                                  ;
; volume slides down then up,      ;
; starting at current frequency    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_vol_trough
  mov    bl,[byte edi+tfunk_chan.volume_com_val]
  and    ebx,1111b
  mov    dl,[byte ebx+com_volume_inc]
  cmp    [byte edi+tfunk_chan.volume_beat_count],1
  je     @@wait_a_bit
  cmp    [byte edi+tfunk_chan.volume_beat_count],2
  je     @@slide_down
  shr    dl,2
  inc    dl
  sub    [byte edi+tfunk_chan.volume],dl
  jc     @@second_phase
@@done:
  call   vol_to_realvol
  call   [dword CARD_volume_convert]
  ret
@@second_phase:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  inc    [byte edi+tfunk_chan.volume_beat_count]
  mov    [byte edi+tfunk_chan.volume],0
  jmp    @@done
@@wait_a_bit:
  inc    bl
  neg    bl
  and    bl,1111b
  cmp    [byte edi+tfunk_chan.volume_comspd_count],bl
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.volume_comspd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  inc    [byte edi+tfunk_chan.volume_beat_count]
  ret
@@slide_down:
  mov    al,[byte edi+tfunk_chan.volume_portdest]
  add    [byte edi+tfunk_chan.volume],dl
  jc     @@end
  cmp    [byte edi+tfunk_chan.volume],al
  jb     @@done
@@end:
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume],al
  jmp    @@done
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OA: master volume set        ;
;                                  ;
; master effects all volumes, good ;
; for fading songs in and out      ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_master_set
  mov    al,[byte edi+tfunk_chan.com_val]
  and    al,1111b
  mov    [byte funk_info.master_volume],al
  push   edi
  lea    edi,[funk_chan1]
  mov    cl,8
@@sync_volume:
  push   ecx
  call   [dword CARD_volume_convert]
  add    edi,size tfunk_chan
  pop    ecx
  dec    cl
  jnz    @@sync_volume
  pop    edi
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OB: expand loop              ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_expand_loop
  mov    al,[byte edi+tfunk_chan.com_val]
  and    eax,1111b
  inc    al
  mov    cl,[byte edi+tfunk_chan.sample_ofs_parm]
  shl    eax,cl
  sub    [dword edi+tfunk_chan.start],eax
  or     [byte edi+tfunk_chan.funkctrl],10000000b
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OC: colapse loop             ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_colapse_loop
  mov    al,[byte edi+tfunk_chan.com_val]
  and    eax,1111b
  inc    al
  mov    cl,[byte edi+tfunk_chan.sample_ofs_parm]
  shl    eax,cl
  add    [dword edi+tfunk_chan.start],eax
  or     [byte edi+tfunk_chan.funkctrl],10000000b
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OD: note retrig              ;
;                                  ;
; com_val : ????   0000            ;
;                 \speed/ (prehand);
;                                  ;
; command only handled on full     ;
; or sample only slots             ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_note_retrig
  mov    al,[byte edi+tfunk_chan.com_val]
  and    al,00001111b
  cmp    [byte edi+tfunk_chan.retrig_spd_count],al
  jae    @@speed_match
  inc    [byte edi+tfunk_chan.retrig_spd_count]
  ret
@@speed_match:
  mov    [byte edi+tfunk_chan.retrig_spd_count],0

  mov    al,[byte edi+tfunk_chan.retrig_limit]
  cmp    [byte edi+tfunk_chan.retrig_count],al
  jae    @@done
  inc    [byte edi+tfunk_chan.retrig_count]
  mov    al,[byte edi+tfunk_chan.sample]
  call   do_retrig_sample
  ret
@@done:
  mov    [byte edi+tfunk_chan.command],0fh
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OE: set channel balance      ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_balance
  mov    al,[byte edi+tfunk_chan.com_val]
  shl    al,4
  mov    [byte edi+tfunk_chan.balance],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Com OF: tempo                    ;
;                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_tempo
  mov    al,[byte edi+tfunk_chan.com_val]
  and    al,1111b
  mov    [byte funk_info.tempo],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Command control for null slots only                                      ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_null_ctrl
  mov    eax,[dword edi+tfunk_chan.command]
  and    eax,1111111100001111b
  cmp    al,0fh
  je     @@command_idle
  or     al,al                                    ; com a
  je     @@ns_basic
  cmp    al,01h                                   ; com b
  je     @@ns_basic
  cmp    al,02h                                   ; com c
  je     @@ns_basic
  cmp    al,03h                                   ; com d
  je     @@ns_basic
  cmp    al,04h                                   ; com e
  je     @@command_e
  cmp    al,05h                                   ; com f
  je     @@command_f
  cmp    al,06h                                   ; com g
  je     @@vs_basic
  cmp    al,07h                                   ; com h
  je     @@vs_basic
  cmp    al,08h                                   ; com i
  je     @@vs_basic
  cmp    al,09h                                   ; com j
  je     @@command_j
  cmp    al,0ah                                   ; com k
  je     @@vs_basic
  cmp    al,0bh                                   ; com l
  je     @@command_l
  cmp    al,0dh                                   ; com n
  je     @@command_n
  mov    ebx,eax
  and    bh,11110000b
  cmp    ebx,0000eh                               ; com o0
  je     @@command_o0
  cmp    ebx,0100eh                               ; com o1
  je     @@vs_basic
  cmp    ebx,0200eh                               ; com o2
  je     @@command_o2
  cmp    ebx,0300eh                               ; com o3
  je     @@command_o3
  cmp    ebx,0400eh                               ; com o4
  je     @@command_o4
  cmp    ebx,0500eh                               ; com o5
  je     @@command_o5
  cmp    ebx,0600eh                               ; com o6
  je     @@command_o6
  cmp    ebx,0700eh                               ; com o7
  je     @@command_o7
  cmp    ebx,0800eh                               ; com o8
  je     @@command_o8o9
  cmp    ebx,0900eh                               ; com o9
  je     @@command_o8o9
  cmp    ebx,0a00eh                               ; com oa
  je     @@command_oa
  cmp    ebx,0b00eh                               ; com ob
  je     @@command_ob
  cmp    ebx,0c00eh                               ; com oc
  je     @@command_oc
  cmp    ebx,0e00eh                               ; com oe
  je     @@command_oe
  cmp    ebx,0f00eh                               ; com of
  je     @@command_of
  ret
@@ns_basic:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  ret
@@vs_basic:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  ret
@@command_e:
  mov    [byte edi+tfunk_chan.note_beat_count],0
  jmp    @@set_vib_fan
@@command_f:
  mov    [byte edi+tfunk_chan.note_beat_count],0fh
@@set_vib_fan:
  cmp    [byte edi+tfunk_chan.note_command],03h  ; if vibrato command
  jne    @@dont_set_vib
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  shr    bl,4
  mov    [byte edi+tfunk_chan.note_beat_count],bl
@@dont_set_vib:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  ret
@@command_j:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  mov    al,[byte edi+tfunk_chan.volume]
  mov    [byte edi+tfunk_chan.volume_beat_count],al
  ret
@@command_l:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  mov    [byte edi+tfunk_chan.note_beat_count],1
  ret
@@command_n:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   com_volume
  ret
@@command_o0:
  call   com_misccrtl
  ret
@@command_o2:
  call   com_real_freqad
  call   [dword CARD_volume_convert]
  ret
@@command_o3:
  call   com_arp_speed_set
  ret
@@command_o4:
  mov    [word edi+tfunk_chan.note_command],ax
  call   com_fine_port_up
  ret
@@command_o5:
  mov    [word edi+tfunk_chan.note_command],ax
  call   com_fine_port_dn
  ret
@@command_o6:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   com_vol_up
  ret
@@command_o7:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   com_vol_dn
  ret
@@command_o8o9:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  mov    [byte edi+tfunk_chan.volume_beat_count],0
  mov    al,[byte edi+tfunk_chan.volume]
  mov    [byte edi+tfunk_chan.volume_portdest],al
  ret
@@command_oa:
  call   com_master_set
  ret
@@command_ob:
  call   com_expand_loop
  ret
@@command_oc:
  call   com_colapse_loop
  ret
@@command_oe:
  call   com_balance
  ret
@@command_of:
  call   com_tempo
  ret
@@command_idle:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Command control for full slots                                           ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_full_ctrl
  mov    eax,[dword edi+tfunk_chan.command]
  and    eax,1111111100001111b
  cmp    al,0fh
  je     @@command_idle
  or     al,al                                    ; com a
  je     @@ns_basic
  cmp    al,01h                                   ; com b
  je     @@ns_basic
  cmp    al,02h                                   ; com c
  je     @@command_c
  cmp    al,03h                                   ; com d
  je     @@ns_basic
  cmp    al,04h                                   ; com e
  je     @@command_e
  cmp    al,05h                                   ; com f
  je     @@command_f
  cmp    al,06h                                   ; com g
  je     @@vs_basic
  cmp    al,07h                                   ; com h
  je     @@vs_basic
  cmp    al,08h                                   ; com i
  je     @@command_i
  cmp    al,09h                                   ; com j
  je     @@command_j
  cmp    al,0ah                                   ; com k
  je     @@vs_basic
  cmp    al,0bh                                   ; com l
  je     @@command_l
  cmp    al,0ch                                   ; com m
  je     @@command_m
  cmp    al,0dh                                   ; com n
  je     @@command_n
  mov    ebx,eax
  and    bh,11110000b
  cmp    ebx,0000eh                               ; com o0
  je     @@command_o0
  cmp    ebx,0100eh                               ; com o1
  je     @@vs_basic
  cmp    ebx,0200eh                               ; com o2
  je     @@command_o2
  cmp    ebx,0300eh                               ; com o3
  je     @@command_o3
  cmp    ebx,0400eh                               ; com o4
  je     @@command_o4
  cmp    ebx,0500eh                               ; com o5
  je     @@command_o5
  cmp    ebx,0600eh                               ; com o6
  je     @@command_o6
  cmp    ebx,0700eh                               ; com o7
  je     @@command_o7
  cmp    ebx,0800eh                               ; com o8
  je     @@command_o8o9
  cmp    ebx,0900eh                               ; com o9
  je     @@command_o8o9
  cmp    ebx,0a00eh                               ; com oa
  je     @@command_oa
  cmp    ebx,0b00eh                               ; com ob
  je     @@command_ob
  cmp    ebx,0c00eh                               ; com oc
  je     @@command_oc
  cmp    ebx,0d00eh                               ; com od
  je     @@command_od
  cmp    ebx,0e00eh                               ; com oe
  je     @@command_oe
  cmp    ebx,0f00eh                               ; com of
  je     @@command_of
  ret
@@ns_basic:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  call   normal_decode_note
  ret
@@vs_basic:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  call   normal_decode_volume
  ret
@@command_c:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  call   comc_decode
  ret
@@command_e:
  mov    [byte edi+tfunk_chan.note_beat_count],0
  jmp    @@set_vib_fan
@@command_f:
  mov    [byte edi+tfunk_chan.note_beat_count],0fh
@@set_vib_fan:
  cmp    [byte edi+tfunk_chan.note_command],03h  ; if vibrato command
  jne    @@dont_set_vib
  mov    bl,[byte edi+tfunk_chan.note_com_val]
  shr    bl,4
  mov    [byte edi+tfunk_chan.note_beat_count],bl
@@dont_set_vib:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  call   normal_decode_note
  ret
@@command_i:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  call   comi_decode
  ret
@@command_j:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  call   normal_decode_volume
  mov    al,[byte edi+tfunk_chan.volume]
  mov    [byte edi+tfunk_chan.volume_beat_count],al
  ret
@@command_l:
  mov    [word edi+tfunk_chan.note_command],ax
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  mov    [byte edi+tfunk_chan.note_beat_count],1
  call   normal_decode_note
  ret
@@command_m:
  call   normal_decode_system
  call   com_sample_offset
  ret
@@command_n:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   normal_decode_volume
  call   com_volume
  ret
@@command_o0:
  call   normal_decode_system
  call   com_misccrtl
  ret
@@command_o2:
  call   com_real_freqad
  call   normal_decode_system
  ret
@@command_o3:
  call   normal_decode_system
  call   com_arp_speed_set
  ret
@@command_o4:
  mov    [word edi+tfunk_chan.note_command],ax
  call   normal_decode_note
  call   com_fine_port_up
  ret
@@command_o5:
  mov    [word edi+tfunk_chan.note_command],ax
  call   normal_decode_note
  call   com_fine_port_dn
  ret
@@command_o6:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   normal_decode_volume
  call   com_vol_up
  ret
@@command_o7:
  mov    [word edi+tfunk_chan.volume_command],ax
  call   normal_decode_volume
  call   com_vol_dn
  ret
@@command_o8o9:
  mov    [word edi+tfunk_chan.volume_command],ax
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  mov    [byte edi+tfunk_chan.volume_beat_count],0
  call   normal_decode_volume
  mov    al,[byte edi+tfunk_chan.volume]
  mov    [byte edi+tfunk_chan.volume_portdest],al
  ret
@@command_oa:
  call   com_master_set
  call   normal_decode_system
  ret
@@command_ob:
  call   normal_decode_system
  call   com_expand_loop
  ret
@@command_oc:
  call   normal_decode_system
  call   com_colapse_loop
  ret
@@command_od:
  mov    [byte edi+tfunk_chan.retrig_count],1
  mov    [byte edi+tfunk_chan.retrig_spd_count],0
  call   normal_decode_system
  ret
@@command_oe:
  call   normal_decode_system
  call   com_balance
  ret
@@command_of:
  call   normal_decode_system
  call   com_tempo
  ret
@@command_idle:
  call   normal_decode_system
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; Command control for rapid "on the fly"                                   ;
; Rapid commands that have a speed parameter                               ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc com_rapid_ctrl
  lea    edi,[funk_chan1]
  mov    cl,8
@@play_slot:
  push   ecx

  call   note_system_rapid_p1
  call   volume_system_rapid_p1

  mov    eax,[dword edi+tfunk_chan.note_command]
  cmp    al,03h                                   ; command d
  je     @@command_d
  cmp    al,04h                                   ; command e
  je     @@command_e
  cmp    al,05h                                   ; command f
  je     @@command_f
  cmp    al,0bh                                   ; command l
  je     @@command_l
  jmp    @@do_vol_com
@@command_d:
  call   com_vibrato
  jmp    @@do_vol_com
@@command_e:
  call   com_vib_fanin
  jmp    @@do_vol_com
@@command_f:
  call   com_vib_fanout
  jmp    @@do_vol_com
@@command_l:
  call   com_arpeggio
@@do_vol_com:
  mov    eax,[dword edi+tfunk_chan.volume_command]
  cmp    al,09h                                   ; command j
  je     @@command_j
  cmp    al,0Ah                                   ; command k
  je     @@command_k
  and    eax,1111000000001111b
  cmp    eax,0100eh                               ; command o1
  je     @@command_o1
  cmp    eax,0800eh                               ; command o8
  je     @@command_o8
  cmp    eax,0900eh                               ; command o9
  je     @@command_o9
  jmp    @@system_final
@@command_j:
  call   com_reverb
  jmp    @@system_final
@@command_k:
  call   com_tremola
  jmp    @@system_final
@@command_o1:
  call   com_volume_cut
  jmp    @@system_final
@@command_o8:
  call   com_vol_crest
  jmp    @@system_final
@@command_o9:
  call   com_vol_trough
@@system_final:
  mov    eax,[dword edi+tfunk_chan.command]
  and    eax,1111000000001111b
  cmp    eax,0d00eh                               ; command od
  jne    @@end_command
  call   com_note_retrig
@@end_command:
  pop    ecx
  add    edi,size tfunk_chan
  dec    cl
  jnz    @@play_slot
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; ESI = current pattern                                                    ;
;                                                                          ;
; format:                                                                  ;
;                                                                          ;
; 00000000 11111111 22222222                                               ;
; \    /\     /\  / \      /                                               ;
;  note  sample com  command value                                         ;
;                                                                          ;
; - if note:  = 3D, then reload sample attrs                               ;
;             = 3F, then it's a null slot                                  ;
;             = 3E, then sample only slot                                  ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc trekk_slot
  movzx  eax,[byte funk_info.pattern_ofs]         ;24 * al
  mov    ebx,eax
  shl    eax,4
  shl    ebx,3
  add    eax,ebx
  add    esi,eax

  lea    edi,[funk_chan1]
  mov    cl,8
@@play_slot:
  push   ecx esi
  mov    ax,[word esi+1]                          ;extract command and value
  and    al,00001111b
  mov    [word edi+tfunk_chan.command],ax

  mov    al,[byte esi]
  shr    al,2
  cmp    al,03Fh                                  ;IF FULL SLOT or SAMPLE
  jne    @@full_cont                              ;ONLY SLOT
  call   com_null_ctrl
  jmp    @@next_channel
@@full_cont:
  call   com_full_ctrl
@@next_channel:
  pop    esi ecx
  add    edi,size tfunk_chan
  add    esi,3
  dec    cl
  jnz    @@play_slot
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; The actual tracker thingy :) ....0.02 second tick (or roughly)           ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc funk_tracker
  cmp    [byte funk_info.trek_status],STOP
  je     @@dont_play
  call   com_rapid_ctrl
  mov    al,[byte funk_info.tempo]
  cmp    [byte funk_info.tempo_count],al
  jb     @@tempo_miss
  mov    [byte funk_info.tempo_count],0
  xor    ebx,ebx
  mov    bl,[byte funk_info.sequence_ofs]
  mov    edi,[funk_hr_ptr]
  movzx  esi,[byte ebx+edi+tfunk_hr.order_list]
  mov    eax,esi                                  ; bx * 600h
  shl    eax,10
  shl    esi,9
  add    esi,eax
  add    esi,edi
  add    esi,size tfunk_hr
  call   trekk_slot
  mov    edi,[funk_hr_ptr]
  xor    ebx,ebx
  mov    bl,[byte funk_info.sequence_ofs]
  movzx  ebx,[byte ebx+edi+tfunk_hr.order_list]
  mov    al,[byte ebx+edi+tfunk_hr.break_list]
  cmp    [byte funk_info.pattern_ofs],al          ;if done last pattern then...
  je     @@change_pattern                         ;jump if no pattern cross
  inc    [byte funk_info.pattern_ofs]             ;add the trek slot ofs
  ret
@@change_pattern:
  mov    cl,[byte funk_info.no_of_sequences]
  cmp    [byte funk_info.sequence_ofs],cl
  jb     @@dont_loop_seq_ofs
  cmp    [byte edi+tfunk_hr.loop_order],0FFh
  jne    @@set_loop_rippp
  mov    [byte funk_info.trek_status],STOP
@@set_loop_rippp:
  mov    dl,[byte edi+tfunk_hr.loop_order]
  dec    dl
  mov    [byte funk_info.sequence_ofs],dl
@@dont_loop_seq_ofs:
  inc    [byte funk_info.sequence_ofs]
  mov    [byte funk_info.pattern_ofs],0
  ret
@@tempo_miss:
  inc    [byte funk_info.tempo_count]
@@dont_play:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc stop_all_voices
  cli
  lea    edi,[funk_chan1]
  xor    cl,cl
@@play_slot:
  push   ecx
  mov    [byte edi+tfunk_chan.funkctrl],0
  mov    [byte edi+tfunk_chan.volume],0
  mov    [byte edi+tfunk_chan.rvolume],0
  call   [dword CARD_volume_convert]
  pop    ecx
  add    edi,size tfunk_chan
  inc    cl
  cmp    cl,8
  jb     @@play_slot
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; MPU-401 UART Code (Midi keyboard code)                                   ;
;                                                                          ;
; (NB/ most of the MPU code is from the CT SDK kit V2.0)                   ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MPU_addr              dd 330h

struc tMIDI
  midi_status         db STOP
  midi_buffer         db 32 dup(?)  ; 32 byte circulur buffer
  midi_buffer_head    dd 0
  midi_buffer_tail    dd 0
  base                db 0
  byte1               db 0
  byte2               db 0
  byte3               db 0
ends
MIDI                  tMIDI <>

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MIDI_queue_in
  mov    ebx,[MIDI.midi_buffer_head]
  mov    [byte ebx+MIDI.midi_buffer],al
  inc    [MIDI.midi_buffer_head]
  and    [MIDI.midi_buffer_head],31
  ret
endp

proc MIDI_queue_out_pol
@@wait:
  mov    ebx,[MIDI.midi_buffer_tail]
  cmp    ebx,[MIDI.midi_buffer_head]
  je     @@wait
  mov    al,[byte ebx+MIDI.midi_buffer]
  inc    [MIDI.midi_buffer_tail]
  and    [MIDI.midi_buffer_tail],31
  ret
endp

proc MIDI_queue_out_sig
  mov    ebx,[MIDI.midi_buffer_tail]
  cmp    ebx,[MIDI.midi_buffer_head]
  je     @@ignore
  mov    al,[byte ebx+MIDI.midi_buffer]
  inc    [MIDI.midi_buffer_tail]
  and    [MIDI.midi_buffer_tail],31
  clc
  ret
@@ignore:
  stc
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ResetMPU401                                                              ;
;     Reset MPU-401 (for SB16 only)                                        ;
; Entry   :                                                                ;
;     none.                                                                ;
; Exit    :                                                                ;
;     EAX = 0 if successful else 1.                                        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MPU401_Reset
  cli
  mov    edx,[MPU_addr]
  inc    edx
  xor    ecx,ecx
@@Busy1:
  in     al,dx
  test   al,40h
  jz     @@ok1
  dec    cx
  jnz    @@Busy1
  jmp    @@fail
@@ok1:
  mov    al,0FFh
  out    dx,al
  sub    ecx,ecx
@@Empty1:
  in     al,dx
  test   al,80h
  jnz    @@NesLoop1

  dec    edx
  in     al,dx
  cmp    al,0FEh
  mov    eax,00h
  je     @@ResetOK1
  inc    edx
@@NesLoop1:
  dec    cx
  jnz    @@Empty1
@@fail:
  mov    eax,01H
@@ResetOK1:
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; EnterUARTModeMPU401                                                      ;
;     Set MPU-401 to UART mode.                                            ;
; Entry   :                                                                ;
;     none.                                                                ;
; Exit    :                                                                ;
;     EAX = 0 if successful else 1.                                        ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MPU401_EUARTM
  cli
  mov    edx,[MPU_addr]
  inc    edx
@@Busy2:
  in     al,dx
  test   al,40h
  jnz    @@Busy2
  mov    al,3Fh
  out    dx,al
  sub    ecx,ecx
@@Empty2:
  in     al,dx
  test   al,80h
  jnz    @@NesLoop2
  dec    edx
  in     al,dx
  cmp    al,0FEh
  je     @@InUartMode2
  inc    edx
@@NesLoop2:
  dec    cx
  jnz    @@Empty2
  mov    eax,01h
  jmp    @@exit
@@InUartMode2:
  mov    eax,00h
@@exit:
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WriteMPU401                                                              ;
;     Write a byte of MIDI data to MPU-401                                 ;
; Entry :                                                                  ;
;     al      = output data.                                               ;
; Exit :                                                                   ;
;     none.                                                                ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MPU401_Write
  cli
  push   edx
  mov    edx,[MPU_addr]
  inc    edx
  push   eax
@@Busy3:
  in     al,dx
  test   al,40h
  jnz    @@Busy3
  dec    edx
  pop    eax
  out    dx,al
  pop    edx
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ReadMPU401                                                               ;
;     Read a byte of MIDI data from MPU-401                                ;
; Entry :                                                                  ;
;     none.                                                                ;
; Exit :                                                                   ;
;     al      = input data.                                                ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MPU401_Read
  cli
  push   edx
  mov    edx,[MPU_addr]
  inc    edx
@@Busy4:
  in     al,dx
  test   al,80H
  jnz    @@Busy4
  dec    edx
  in     al,dx
  pop    edx
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; DMA routines (From DOS32)                                                ;
; INPUT:        AL    Mode Register  ( bits 0..1 ignored )                 ;
;               AH    channel    0..7                                      ;
;               EBX   Physical Base Address                                ;
;               ECX   Bytes to transfer                                    ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc DMA_Setup
  xor     edx,edx
  and     ah,7
  mov     [@@DMA_channel],ah
  and     al,NOT 3
  mov     [@@mode],al
  movzx   edi,[byte @@DMA_channel]
  mov     eax,edi
  shr     edi,2
  and     al,0011b
  or      al,0100b
  mov     dl,[edi+@@DMA_SNGL]
  out     dx,al
  call    @@pause
  and     al,03h
  or      al,[@@Mode]
  mov     dl,[edi+@@DMA_MODE]
  out     dx,al
  call    @@pause
  mov     dl,[edi+@@DMA_CLRFF]
  out     dx,al
  call    @@pause
  movzx   edi,[@@DMA_channel]
  mov     eax,ecx
  mov     ecx,edi
  shr     ecx,2
  shr     eax,cl
  mov     dl,[edi+@@DMA_CNT]
  out     dx,al
  call    @@pause
  shr     eax,8
  out     dx,al
  call    @@pause
  shr     ebx,cl
  mov     al,BL
  mov     dl,[edi+@@DMA_ADDR]
  out     dx,al
  call    @@pause
  mov     al,BH
  out     dx,al
  call    @@pause
  shr     ebx,15
  xor     cl,1
  shr     ebx,cl
  mov     al,BL
  mov     dl,[edi+@@DMA_PAGE]
  out     dx,al
  call    @@pause
  mov     eax,edi
  shr     edi,2
  and     al,03h
  mov     dl,[edi+@@DMA_SNGL]
  out     dx,al
  call    @@pause
  ret
@@Mode                db  ?
@@DMA_Channel         db  ?
@@DMA_STAT            db 008h,0D0h
@@DMA_CMD             db 008h,0D0h
@@DMA_REQ             db 009h,0D2h
@@DMA_SNGL            db 00Ah,0D4h
@@DMA_MODE            db 00Bh,0D6h
@@DMA_CLRFF           db 00Ch,0D8h
@@DMA_MCLR            db 00Dh,0DAh
@@DMA_CLRM            db 00Eh,0DCh
@@DMA_WRTALL          db 00Fh,0DEh
@@DMA_PAGE            db 087h,083h,081h,082h,08Fh,08Bh,089h,08Ah
@@DMA_ADDR            db 000h,002h,004h,006h,0C0h,0C4h,0C8h,0CCh
@@DMA_CNT             db 001h,003h,005h,007h,0C2h,0C6h,0CAh,0CEh
proc @@pause
  jmp    @@j
@@j:
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; DAC INDEPENDANT CODE                                                     ;
;                                                                          ;
; These routines are general DAC routines used by all DAC soundcards.      ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
DAC_old_irq_ofs       dd 0
DAC_old_irq_sel       dw 0
DAC_mix_buffer_size   dd ?
DAC_sampling_rate     dd ?
DMA_length            dd 4000h

DAC_mb_offs           = 0
DAC_mb_right_offs     = 1000h
DAC_mb2_offs          = 2000h
DAC_mix_buffer        dd DAC_mb_offs
DAC_mix_buffer_right  dd DAC_mb_right_offs
DAC_mix_buffer2       dd DAC_mb2_offs
DAC_PHYSICAL_PAGE     dd DAC_mb2_offs
DAC_shift_value       db 0
DAC_double_buf1       dd 0
DAC_double_buf2       dd 0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc DACi_freq_convert
  mov    eax,[dword edi+tfunk_chan.rfreq]
  mov    edx,eax
  shl    eax,16
  shr    edx,16
  div    [dword DAC_sampling_rate]
  mov    [dword edi+tfunk_chan.CARD_freq],eax
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc DACi_volume_convert
  xor    eax,eax
  cmp    [byte edi+tfunk_chan.channel_kill],STOP
  je     @@transt
  mov    al,[byte funk_info.master_volume]
  cmp    al,0
  je     @@transt
  xor    ecx,ecx
  mov    cl,[byte edi+tfunk_chan.rvolume]
  inc    al
  mul    ecx
  mov    cl,[byte DAC_shift_value]
  shr    eax,cl
@@transt:
  mov    [byte edi+tfunk_chan.CARD_volume],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; unit channel DAC buffer Mixer                                            ;
;                                                                          ;
; ebx = channel ptr, edi = buffer                                          ;
;                                                                          ;
; 00                                                                       ;
; ||_____ 1=loop sample                                                    ;
; |                                                                        ;
; |______ DAC sample play                                                  ;
;                                                                          ;
; =====================================================================    ;
;                                                                          ;
; This is the heart of DAC mixxing. Here we actually mix a given channel   ;
; to the mixing buffer. This code does frequency transformations and volume;
; manipulation (it's what the DSP cards do interally). If a Card does this ;
; in hardware, then it is refered to as a "DSP" card rather than a "DAC"   ;
; card. An example of a "DSP" card is the Gravis Ultrasound card.          ;
;                                                                          ;
; The frequency transformer belongs to Tom Verbeure...thanks tom!          ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc DAC_channel_mixxer
  mov    ch,[byte ebx+tfunk_chan.funkctrl]
  mov    cl,[byte ebx+tfunk_chan.CARD_volume]
  mov    edx,[dword ebx+tfunk_chan.CARD_freq]
  mov    ebp,edx
  shl    edx,16
  shr    ebp,16
  mov    esi,[dword ebx+tfunk_chan.CARD_sample_ptr]
  mov    eax,[dword DAC_mix_buffer_size]
@@mix_loop:
  push   eax
  test   ch,10b
  jnz    @@run_sample
  xor    al,al
  jmp    @@trans_sample
@@run_sample:
  mov    al,[byte esi]
@@trans_sample:
  imul   cl
  add    [word edi],ax
  add    edi,2
  add    [dword ebx+tfunk_chan.CARD_freq_attract],edx
  adc    esi,ebp
  cmp    esi,[dword ebx+tfunk_chan.length]
  jb     @@comp_end
  test   ch,01b
  jnz    @@loop_back
  and    ch,01b
  jmp    @@comp_end
@@loop_back:
  mov    esi,[dword ebx+tfunk_chan.start]
@@comp_end:
  pop    eax
  dec    eax
  jnz    @@mix_loop
  mov    [byte ebx+tfunk_chan.funkctrl],ch
  mov    [dword ebx+tfunk_chan.CARD_sample_ptr],esi
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; SB DEPENDANT CODE                                                        ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SB_INTR_8BITDMA        = 1
SB_INTR_16BITDMA       = 2
SB_INTR_MPU401         = 4

SB_BASE                dd ?
SB_MIXER               dd ?
SB_RESET               dd ?
SB_READ_DATA           dd ?
SB_WRITE_DATA          dd ?
SB_DATA_AVAIL          dd ?
SB_DATA_AVAIL16        dd ?

SB_trig                dd ?

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB_detect_port
  mov    eax,[dword init_settings.PORT_no]
  mov    [dword SB_BASE],0
  mov    [dword SB_MIXER],4h
  mov    [dword SB_RESET],6h
  mov    [dword SB_READ_DATA],0ah
  mov    [dword SB_WRITE_DATA],0ch
  mov    [dword SB_DATA_AVAIL],0eh
  mov    [dword SB_DATA_AVAIL16],0fh
  add    [dword SB_BASE],eax
  add    [dword SB_MIXER],eax
  add    [dword SB_RESET],eax
  add    [dword SB_READ_DATA],eax
  add    [dword SB_WRITE_DATA],eax
  add    [dword SB_DATA_AVAIL],eax
  add    [dword SB_DATA_AVAIL16],eax
  lea    eax,[DACi_freq_convert]
  mov    [dword CARD_freq_convert],eax
  lea    eax,[DACi_volume_convert]
  mov    [dword CARD_volume_convert],eax
  mov    al,1
  mov    edx,[dword SB_RESET]
  out    dx,al
  in     al,dx
  in     al,dx
  in     al,dx
  in     al,dx
  xor    al,al
  mov    edx,[dword SB_RESET]
  out    dx,al
  mov    cl,64h
@@DataWait:
  mov    edx,[dword SB_DATA_AVAIL]
  in     al,dx
  test   al,80h
  jnz    @@YesData
  dec    cl
  jnz    @@DataWait
  jmp    @@exit
@@YesData:
  mov    edx,[dword SB_READ_DATA]
  in     al,dx
  cmp    al,0AAh
  je     @@YepSB
  dec    cl
  jnz    @@DataWait
  jmp    @@exit
@@YepSB:
  clc
  ret
@@exit:
  stc
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB_write_dac
  push   eax
  mov    edx,[dword SB_WRITE_DATA]
@@Wait_Ready:
  in     al,dx
  and    al,80h
  jnz    @@Wait_Ready
  pop    eax
  out    dx,al
  ret
endp

proc SB_read_dac
  mov    edx,[dword SB_DATA_AVAIL]
@@Wait_Ready:
  in     al,dx
  or     al,al
  jns    @@Wait_Ready
  sub    edx,4
  in     al,dx
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  al = addr  ah = data      SBPRO mixxer                                  ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB_write_mixer
  mov    edx,[dword SB_MIXER]
  push   edx
  out    dx,al
  pop    edx
  inc    edx
  mov    al,ah
  out    dx,al
  ret
endp

proc SB_read_mixer
  mov    edx,[dword SB_MIXER]
  push   edx
  out    dx,al
  pop    edx
  inc    edx
  in     al,dx
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; DMA trigs                                                                ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB_trig_compatable
  mov    al,14h                                        ;DMA_8_BIT_DAC
  call   SB_write_dac
  mov    eax,[dword DMA_length]
  dec    eax
  call   SB_write_dac
  mov    al,ah
  call   SB_write_dac
  ret
endp

proc SB_trig_advanced
  mov    al,91h
  call   SB_write_dac
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB_virtualmixxer
  cld
  push   ds es
  pushad
  mov    ax,DGROUP
  mov    ds,ax
  mov    es,ax
  mov    edx,[dword SB_DATA_AVAIL]
  in     al,dx
  call   [dword SB_trig]
  mov    esi,[dword DAC_mix_buffer]
  mov    edi,[dword DAC_mix_buffer2]
  add    edi,[DAC_double_buf1]
  mov    eax,[DAC_double_buf1]
  xchg   eax,[DAC_double_buf2]
  mov    [DAC_double_buf1],eax
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
@@trans8:
  mov    eax,[esi]
  mov    al,ah
  mov    [edi],al
  mov    eax,[esi+2]
  mov    al,ah
  mov    [edi+1],al
  add    esi,4
  add    edi,2
  dec    ecx
  jnz    @@trans8
  call   funk_tracker
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer]
  mov    eax,80008000h
  rep    stosd
  lea    ebx,[funk_chan1]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan2]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan3]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan4]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan5]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan6]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan7]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan8]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  mov    al,20h
  out    20h,al
  popad
  pop    es ds
  iretd
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SBPRO_virtualmixxer
  cld
  push   ds es
  pushad
  mov    ax,DGROUP
  mov    ds,ax
  mov    es,ax
  mov    edx,[dword SB_DATA_AVAIL]
  in     al,dx
  call   [dword SB_trig]
  mov    esi,[dword DAC_mix_buffer]
  mov    edi,[dword DAC_mix_buffer2]
  add    edi,[DAC_double_buf1]
  mov    eax,[DAC_double_buf1]
  xchg   eax,[DAC_double_buf2]
  mov    [DAC_double_buf1],eax
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
@@trans8:
  mov    eax,[esi+DAC_mb_right_offs]
  mov    al,ah
  mov    ah,[byte esi+1+DAC_mb_offs]
  mov    [edi],ax
  mov    eax,[esi+2+DAC_mb_right_offs]
  mov    al,ah
  mov    ah,[byte esi+3+DAC_mb_offs]
  mov    [edi+2],ax
  add    esi,4
  add    edi,4
  dec    ecx
  jnz    @@trans8
  call   funk_tracker
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer]
  mov    eax,80008000h
  rep    stosd
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer_right]
  rep    stosd
  lea    ebx,[funk_chan1]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan2]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan3]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan4]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan5]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan6]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan7]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan8]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  mov    al,20h
  out    20h,al
  popad
  pop    es ds
  iretd
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB16_virtualmixxer
  cld
  push   ds es
  pushad
  mov    ax,DGROUP
  mov    ds,ax
  mov    es,ax
@@Poll:
  mov    edx,[SB_MIXER]
  mov    al,82h
  out    dx,al
  inc    edx
  in     al,dx
  test   al,02h
  jnz    @@SB16_DSP_ISA
  test   al,04h
  jnz    @@MPU_401_ISA
  jmp    @@chainpreviousISR
@@SB16_DSP_ISA:
  mov    edx,[dword SB_DATA_AVAIL16]
  in     al,dx
  mov    esi,[dword DAC_mix_buffer]
  mov    edx,[dword DAC_mix_buffer_right]
  mov    edi,[dword DAC_mix_buffer2]
  add    edi,[DAC_double_buf1]
  mov    eax,[DAC_double_buf1]
  xchg   eax,[DAC_double_buf2]
  mov    [DAC_double_buf1],eax
  mov    ecx,[dword DAC_mix_buffer_size]
  xor    ebx,ebx
@@trans16:
  mov    eax,[edx+ebx]
  shl    eax,16
  mov    ax,[word esi+ebx]
  mov    [edi+ebx*2],eax
  add    ebx,2
  dec    ecx
  jnz    @@trans16
  call   funk_tracker
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer]
  xor    eax,eax
  rep    stosd
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer_right]
  rep    stosd
  lea    ebx,[funk_chan1]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan2]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan3]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan4]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan5]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan6]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan7]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan8]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  jmp    @@Poll
@@MPU_401_ISA:
  call   MPU401_Read
  call   MIDI_queue_in
  jmp    @@Poll
@@chainpreviousISR:
@@ExitISR:
  mov    al,20h
  out    20h,al
  popad
  pop    es ds
  iretd
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
label SB_sample_settings tDAC_ssetings
  tDAC_ssetings <11025,00222,222,11200>
  tDAC_ssetings <16538,00332,332,16538>
  tDAC_ssetings <22050,00442,442,22050>
  tDAC_ssetings <33075,00662,662,33075>
  tDAC_ssetings <44100,00882,882,44100>

proc SB_init_compatable
  mov    eax,offset SB_trig_compatable
  mov    [SB_trig],eax

  movzx  eax,[byte init_settings.DAC_Samplerate]
  mov    ecx,size tDAC_ssetings
  mul    ecx

  mov    [byte DAC_shift_value],7
  mov    ebx,[dword eax+SB_sample_settings.DAC_sr]
  mov    [dword DAC_sampling_rate],ebx
  mov    ebx,[dword eax+SB_sample_settings.DAC_mix_buffer_size]
  mov    [dword DAC_mix_buffer_size],ebx
  mov    ebx,[dword eax+SB_sample_settings.DMA_length]
  mov    [dword DMA_length],ebx
  mov    eax,[dword eax+SB_sample_settings.DMA_real_sr]
  push   eax
  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[SB_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  xor    edx,edx                                       ;set sampling rate
  mov    eax,1000000
  pop    ecx
  div    ecx
  neg    al
  push   eax
  mov  al,40h                          ;TIME_CONSTANT
  call SB_write_dac
  pop  eax
  call SB_write_dac
  mov  al,0D1h                        ;SPEAKER_ON
  call SB_write_dac
  cli
  in     al,21h
  mov    bl,1
  mov    cl,[byte init_settings.IRQ_no]
  shl    bl,cl
  not    bl
  and    al,bl
  out    21h,al
  mov    al,01011000b                                  ;DMA remote-init
  mov    ah,[byte init_settings.DMA_no]
  mov    ebx,[dword DAC_PHYSICAL_PAGE]
  mov    ecx,[dword DMA_length]
  dec    ecx
  call   DMA_setup
  call   SB_trig_compatable
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

label SBPRO_sample_settings tDAC_ssetings       ; NB/ SBPRO can't do more than 22050
  tDAC_ssetings <11025,00222,0444,23000>
  tDAC_ssetings <16538,00332,0664,35000>
  tDAC_ssetings <22050,00442,0884,88200>
  tDAC_ssetings <22050,00442,0884,88200>
  tDAC_ssetings <22050,00442,0884,88200>

proc sb_init_advanced
  mov    eax,offset SB_trig_advanced
  mov    [SB_trig],eax

  movzx  eax,[byte init_settings.DAC_Samplerate]
  mov    ecx,size tDAC_ssetings
  mul    ecx
  cmp    [byte init_settings.card_type],SBPRO_CARD
  je     @@set_for_SBPRO
  mov    [byte DAC_shift_value],7
  mov    ebx,[dword eax+SB_sample_settings.DAC_sr]
  mov    [dword DAC_sampling_rate],ebx
  mov    ebx,[dword eax+SB_sample_settings.DAC_mix_buffer_size]
  mov    [dword DAC_mix_buffer_size],ebx
  mov    [DAC_double_buf2],ebx
  mov    ebx,[dword eax+SB_sample_settings.DMA_length]
  mov    [dword DMA_length],ebx
  mov    eax,[dword eax+SB_sample_settings.DMA_real_sr]
  push   eax
  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[SB_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  xor    edx,edx                                       ;set sampling rate
  mov    eax,256000000
  pop    ecx
  div    ecx
  mov    ebx,0ffffh
  sub    ebx,eax
  shr    ebx,8
  push   ebx
  jmp    @@done_set
@@set_for_SBPRO:
  push   eax
  mov    [byte DAC_shift_value],6
;-SETUP_PRO MIXXER SETTINGS---------
  mov    eax,00000h                                    ;reset mixxer first
  call   SB_write_mixer
  mov    eax,0020eh
  call   SB_write_mixer
  mov    eax,0ff22h                                    ;set Master volume
  call   SB_write_mixer
  mov    eax,0ff04h                                    ;set DSP volume
  call   SB_write_mixer
;-SETUP_PRO MIXXER SETTINGS---------
  pop    eax
  mov    ebx,[dword eax+SBPRO_sample_settings.DAC_sr]
  mov    [dword DAC_sampling_rate],ebx
  mov    ebx,[dword eax+SBPRO_sample_settings.DAC_mix_buffer_size]
  mov    [dword DAC_mix_buffer_size],ebx
  lea    ebx,[ebx*2]
  mov    [DAC_double_buf2],ebx
  mov    ebx,[dword eax+SBPRO_sample_settings.DMA_length]
  mov    [dword DMA_length],ebx
  mov    eax,[dword eax+SBPRO_sample_settings.DMA_real_sr]
  push   eax
  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[SBPRO_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  xor    edx,edx                                       ;set sampling rate
  mov    eax,256000000
  pop    ecx
  div    ecx
  mov    ebx,0ffffh
  sub    ebx,eax
  shr    ebx,8
  push   ebx
@@done_set:
  mov    al,40h                                        ;TIME_CONSTANT
  call   SB_write_dac
  pop    eax
  call   SB_write_dac
  mov    al,0D1h                                       ;SPEAKER_ON
  call   SB_write_dac
  cli
  in     al,21h
  mov    bl,1
  mov    cl,[byte init_settings.IRQ_no]
  shl    bl,cl
  not    bl
  and    al,bl
  out    21h,al
  mov    al,01011000b                                  ;DMA remote-init
  mov    ah,[byte init_settings.DMA_no]
  mov    ebx,[dword DAC_PHYSICAL_PAGE]
  mov    ecx,[dword DMA_length]
  lea    ecx,[ecx*2]
  dec    ecx
  call   DMA_setup
  mov    al,48h                                        ;DMA_8_BIT_DAC (FAST)
  call   SB_write_dac
  mov    eax,[dword DMA_length]
  dec    eax
  call   SB_write_dac
  mov    al,ah
  call   SB_write_dac
  mov    al,91h
  call   SB_write_dac
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB08_init
  mov    bl,[byte init_settings.IRQ_no]
  call   getirqvector
  mov    [dword DAC_old_irq_ofs],edx
  mov    [word DAC_old_irq_sel],cx

  cmp    [byte init_settings.card_type],SB_CARD
  je     @@set_advanced
  cmp    [byte init_settings.card_type],SBPRO_CARD
  je     @@set_advanced
  cmp    [byte init_settings.card_type],SB15EM_CARD
  je     @@set_compatable
  ret
@@set_compatable:
  call   sb_init_compatable
  ret
@@set_advanced:
  call   sb_init_advanced
  ret
endp


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
; SB1.X, SB2,0 + SBPRO deinit              ;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc sbsub1_deinit
  mov    bl,1
  mov    cl,[byte init_settings.IRQ_no]
  shl    bl,cl
  in     al,21h
  or     al,bl
  out    21h,al
  mov    bl,[byte init_settings.IRQ_no]
  mov    edx,[dword DAC_old_irq_ofs]
  mov    cx,[word DAC_old_irq_sel]
  call   setirqvector
  ret
endp

proc SB08_deinit
  call   SB_detect_port
  cmp    [byte init_settings.card_type],SBPRO_CARD
  jne    @@dont_reset
  mov    eax,110eh                                     ;set for mono
  call   SB_write_mixer
@@dont_reset:
  mov    al,0D0h                                       ;HALT_DMA
  call   SB_write_dac
  cli
  call   sbsub1_deinit
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
label SB16_sample_settings tDAC_ssetings
  tDAC_ssetings <11025,00222,0444,011400>
  tDAC_ssetings <16538,00332,0664,017400>
  tDAC_ssetings <22050,00442,0884,088200>
  tDAC_ssetings <33075,00662,1324,165000>
  tDAC_ssetings <44100,00882,1764,176400>

proc SB16_init
  mov    bl,[byte init_settings.IRQ_no]
  call   getirqvector
  mov    [dword DAC_old_irq_ofs],edx
  mov    [word DAC_old_irq_sel],cx
  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[SB16_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  mov    [byte DAC_shift_value],6
  movzx  eax,[byte init_settings.DAC_Samplerate]
  mov    ecx,size tDAC_ssetings
  mul    ecx
  mov    ebx,[dword eax+SB16_sample_settings.DAC_sr]
  mov    [dword DAC_sampling_rate],ebx
  mov    ebx,[dword eax+SB16_sample_settings.DAC_mix_buffer_size]
  mov    [dword DAC_mix_buffer_size],ebx
  lea    ebx,[ebx*4]
  mov    [DAC_double_buf2],ebx
  mov    ebx,[dword eax+SB16_sample_settings.DMA_length]
  mov    [dword DMA_length],ebx
  mov    eax,[dword eax+SB16_sample_settings.DMA_real_sr]
  push   eax
  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[SB16_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  cli
  in     al,21h
  mov    bl,1
  mov    cl,[byte init_settings.IRQ_no]
  shl    bl,cl
  not    bl
  and    al,bl
  out    21h,al
  mov    al,01011000b
  mov    ah,[byte init_settings.DMA_no]
  mov    ebx,[dword DAC_PHYSICAL_PAGE]
  mov    ecx,[dword DMA_length]
  lea    ecx,[ecx*4]
  dec    ecx
  call   DMA_setup
  mov    al,41h
  call   SB_write_dac
  pop    eax
  xchg   al,ah
  call   SB_write_dac
  mov    al,ah
  call   SB_write_dac
  mov    al,0b6h
  call   SB_write_dac
  mov    al,030h
  call   SB_write_dac
  mov    eax,[dword DMA_length]
  dec    eax
  call   SB_write_dac
  mov    al,ah
  call   SB_write_dac
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SB16_deinit
  cli
  mov    al,0d9h
  call   SB_write_dac
  call   sbsub1_deinit
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; GUS DEPENDANT CODE                                                       ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;GUS CONST
GUS_irq_table          db 0,0,1,2,0,3,0,4,0,0,0,5,6,0,0,7
GUS_dma_table          db 0,1,0,2,0,3,4,5

;Gravis only
gus_base               dd 0
gus_status             dd 6
gus_timercontrol       dd 8
gus_timerdata          dd 9
gus_irqdmacontrol      dd 0bh
gus_midictrl           dd 100h
gus_mididata           dd 101h
gus_voice              dd 102h
gus_command            dd 103h
gus_datalo             dd 104h
gus_datahi             dd 105h
gus_GF1_status         dd 106h
gus_dram               dd 107h
DMA_state              db ?

gus_volume_table:
db 020h,040h,060h,080h,090h,094h,098h,09ch
db 0A0h,0A2h,0A4h,0A6h,0A8h,0AAh,0ACh,0AEh
db 0B0h,0B1h,0B2h,0B3h,0B4h,0B5h,0B6h,0B7h
db 0B8h,0B9h,0BAh,0BBh,0BCh,0BDh,0BEh,0BFh
db 0C0h,0C0h,0C1h,0C1h,0C2h,0C2h,0C3h,0C3h
db 0C4h,0C4h,0C5h,0C5h,0C6h,0C6h,0C7h,0C7h
db 0C8h,0C8h,0C9h,0C9h,0CAh,0CAh,0CBh,0CBh
db 0CCh,0CCh,0CDh,0CDh,0CEh,0CEh,0CFh,0CFh
db 0D0h,0D0h,0D0h,0D0h,0D1h,0D1h,0D1h,0D1h
db 0D2h,0D2h,0D2h,0D2h,0D3h,0D3h,0D3h,0D3h
db 0D4h,0D4h,0D4h,0D4h,0D5h,0D5h,0D5h,0D5h
db 0D6h,0D6h,0D6h,0D6h,0D7h,0D7h,0D7h,0D7h
db 0D8h,0D8h,0D8h,0D8h,0D9h,0D9h,0D9h,0D9h
db 0DAh,0DAh,0DAh,0DAh,0DBh,0DBh,0DBh,0DBh
db 0DCh,0DCh,0DCh,0DCh,0DDh,0DDh,0DDh,0DDh
db 0DEh,0DEh,0DEh,0DEh,0DFh,0DFh,0DFh,0DFh
db 0E0h,0E0h,0E0h,0E0h,0E0h,0E1h,0E1h,0E1h
db 0E1h,0E2h,0E2h,0E2h,0E2h,0E3h,0E3h,0E3h
db 0E3h,0E4h,0E4h,0E4h,0E4h,0E5h,0E5h,0E5h
db 0E5h,0E6h,0E6h,0E6h,0E6h,0E7h,0E7h,0E7h
db 0E7h,0E8h,0E8h,0E8h,0E8h,0E9h,0E9h,0E9h
db 0E9h,0EAh,0EAh,0EAh,0EAh,0EAh,0EBh,0EBh
db 0EBh,0EBh,0ECh,0ECh,0ECh,0ECh,0EDh,0EDh
db 0EDh,0EDh,0EEh,0EEh,0EEh,0EEh,0EFh,0EFh
db 0EFh,0EFh,0F0h,0F0h,0F0h,0F0h,0F1h,0F1h
db 0F1h,0F1h,0F2h,0F2h,0F2h,0F2h,0F3h,0F3h
db 0F3h,0F3h,0F4h,0F4h,0F4h,0F4h,0F4h,0F5h
db 0F5h,0F5h,0F5h,0F6h,0F6h,0F6h,0F6h,0F6h
db 0F7h,0F7h,0F7h,0F7h,0F8h,0F8h,0F8h,0F8h
db 0F9h,0F9h,0F9h,0F9h,0F9h,0F9h,0FAh,0FAh
db 0FAh,0FAh,0FBh,0FBh,0FBh,0FBh,0FBh,0FBh
db 0FBh,0FCh,0FCh,0FCh,0FCh,0FCh,0FCh,0FCh

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; GUS Code                                                                 ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUSi_freq_convert
  mov    eax,[dword edi+tfunk_chan.rfreq]
  xor    edx,edx
  mov    ecx,43
  div    ecx
  mov    [dword edi+tfunk_chan.CARD_freq],eax
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUSi_volume_convert
  xor    eax,eax
  cmp    [byte edi+tfunk_chan.channel_kill],STOP
  je     @@transt
  mov    al,[byte funk_info.master_volume]
  cmp    al,0
  je     @@transt
  xor    ecx,ecx
  mov    cl,[byte edi+tfunk_chan.rvolume]
  inc    al
  mul    ecx
  shr    eax,4
@@transt:
  mov    al,[byte eax+gus_volume_table]
  mov    [byte edi+tfunk_chan.CARD_volume],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; GUS interface routines                                                   ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_set_freq
  mov    edx,[dword gus_command]
  mov    al,1
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.CARD_freq]
  out    dx,ax
  ret
endp

; cl = volume to set
proc GUS_set_volume
  cmp    cl,[byte gus_volume_table]
  jae    @@cont
  ret
@@cont:
  mov    edx,[dword gus_command]
  MOV    al,89h
  OUT    dx,al
  INC    edx
  IN     ax,dx
  mov    bl,ah
  cmp    bl,4
  jae    @@dont_level1
  mov    bl,4
@@dont_level1:
  xor    ch,ch
  cmp    bl,cl
  je     @@end
  jb     @@set_ramp_to_up
  mov    ch,040h
  xchg   bl,cl
@@set_ramp_to_up:
;bl = ramp start
;cl = ramp end
;ch = ramp direction
  DEC    edx
  MOV    al,0dh
  OUT    dx,al
  ADD    edx,2
  MOV    al,3
  OUT    dx,al
  SUB    edx,2
  MOV    al,7
  OUT    dx,al
  ADD    edx,2
  MOV    al,bl
  OUT    dx,al
  SUB    edx,2
  MOV    al,8
  OUT    dx,al
  ADD    edx,2
  mov    al,cl
  OUT    dx,al
  SUB    edx,2
  MOV    al,6
  OUT    dx,al
  ADD    edx,2
  MOV    al,3fh
  OUT    dx,al
  sub    edx,2
  MOV    al,0dh
  OUT    dx,al
  ADD    edx,2
  MOV    al,ch
  OUT    dx,al
@@loopramp:
  sub    edx,2
  mov    al,8dh
  out    dx,al
  add    edx,2
  in     al,dx
  test   al,1
  jz     @@loopramp
@@end:
  ret
endp

proc GUS_load_channel
  mov    cl,[byte gus_volume_table]
  call   GUS_set_volume
  mov    edx,[dword gus_command]
  xor    al,al
  out    dx,al
  add    edx,2
  mov    al,3
  out    dx,al
  mov    eax,[funk_hr_ptr]
  cmp    [word eax+tfunk_hr.LZH_check_sum],"vF"
  jne    @@no_varbalance
  sub    edx,2
  mov    al,0ch
  out    dx,al
  add    edx,2
  mov    al,[byte edi+tfunk_chan.balance]
  shr    al,4
  out    dx,al
@@no_varbalance:
  call   GUS_set_freq
  mov    edx,[dword gus_command]
;Send sample begin
  mov    al,0ah
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.CARD_sample_ptr]
  add    eax,32
  shr    eax,7
  out    dx,ax
  dec    edx
  mov    al,0bh
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.CARD_sample_ptr]
  add    eax,32
  shl    eax,9
  out    dx,ax
  dec    edx
;Send sample start
  mov    al,2
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.start]
  add    eax,32
  shr    eax,7
  out    dx,ax
  dec    edx
  mov    al,3
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.start]
  add    eax,32
  shl    eax,9
  out    dx,ax
  dec    edx
;Send sample end
  mov    al,4
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.length]
  sub    eax,32
  shr    eax,7
  out    dx,ax
  dec    edx
  mov    al,5
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.length]
  sub    eax,32
  shl    eax,9
  out    dx,ax
  mov    cl,[byte edi+tfunk_chan.CARD_volume]
  call   GUS_set_volume
  ret
endp

proc GUS_set_start
  mov    edx,[dword gus_command]
;Send sample start
  mov    al,2
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.start]
  add    eax,32
  shr    eax,7
  out    dx,ax
  dec    edx
  mov    al,3
  out    dx,al
  inc    edx
  mov    eax,[dword edi+tfunk_chan.start]
  add    eax,32
  shl    eax,9
  out    dx,ax
  ret
endp

proc GUS_fire_channel
  cmp    [dword edi+tfunk_chan.length],64
  jae    @@cont
  ret
@@cont:
  mov    edx,[dword gus_command]
  xor    al,al
  out    dx,al
  test   [byte edi+tfunk_chan.funkctrl],1b
  jz     @@dont_loop
  mov    al,8
@@dont_loop:
  add    edx,2
  out    dx,al
  mov    [byte edi+tfunk_chan.funkctrl],0
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_update
  lea    edi,[funk_chan1]
  xor    cl,cl
@@play_slot:
  push   ecx
  mov    al,cl
  mov    edx,[dword gus_voice]
  out    dx,al
  test   [byte edi+tfunk_chan.funkctrl],10b
  jnz    @@fire_channel
  call   GUS_set_freq
  mov    cl,[byte edi+tfunk_chan.CARD_volume]
  call   GUS_set_volume
  test   [byte edi+tfunk_chan.funkctrl],10000000b
  jz     @@done
  and    [byte edi+tfunk_chan.funkctrl],01111111b
  call   GUS_set_start
  jmp    @@done
@@fire_channel:
  call   GUS_load_channel
@@done:
  pop    ecx
  add    edi,size tfunk_chan
  inc    cl
  cmp    cl,0ch
  jb     @@play_slot

  lea    edi,[funk_chan1]
  xor    cl,cl
@@play_slot2:
  mov    al,cl
  mov    edx,[dword gus_voice]
  out    dx,al
  test   [byte edi+tfunk_chan.funkctrl],10b
  jz     @@done2
  call   GUS_fire_channel
@@done2:
  add    edi,size tfunk_chan
  inc    cl
  cmp    cl,0ch
  jb     @@play_slot2
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_mixxer
  cld
  push   ds es
  pushad
  mov    ax,DGROUP
  mov    ds,ax
  mov    es,ax
@@next_irq:
  mov    edx,[dword gus_status]
  in     al,dx
  mov    [byte @@irq_state],al
  test   [byte @@irq_state],10000000b
  jnz    @@DMA_interrupt
  call   funk_tracker
  call   GUS_update
  mov    edx,[dword gus_command]
  mov    al,45h
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    al,8
  out    dx,al
  jmp    @@end
@@DMA_interrupt:
  mov    edx,[dword gus_command]
  mov    al,041h
  out    dx,al
  add    edx,2
  in     al,dx
  or     [byte DMA_state],1
  test   [byte @@irq_state],11101111b
  jz     @@next_irq
@@end:
  mov    al,20h
  out    20h,al
  out    0a0h,al
  popad
  pop    es ds
  iretd
@@irq_state:
  db     ?
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc gus_delay
  mov    edx,[dword gus_dram]
  in     al,dx
  in     al,dx
  in     al,dx
  in     al,dx
  in     al,dx
  in     al,dx
  in     al,dx
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; esi                                                                      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_mem_in
  mov    edx,[dword gus_command]
  mov    al,43h
  out    dx,al
  inc    edx
  mov    eax,esi
  out    dx,ax
  dec    edx
  mov    al,44h
  out    dx,al
  add    edx,2
  mov    eax,esi
  shr    eax,16
  out    dx,al
  add    edx,2
  in     al,dx
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; edi                                                                      ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_mem_out
  push   eax
  mov    edx,[dword gus_Command]
  mov    al,43h
  out    dx,al
  inc    edx
  mov    eax,edi
  out    dx,ax
  dec    edx
  mov    al,44h
  out    dx,al
  add    edx,2
  mov    eax,edi
  shr    eax,16
  out    dx,al
  add    edx,2
  pop    eax
  out    dx,al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_find_mem
  xor    bl,bl
  mov    edi,100h
@@fm_loop:
  mov    al,0AAh
  call   GUS_mem_out
  mov    ecx,0FFFFh
@@bw_loop:
  nop
  dec    ecx
  jnz    @@bw_loop
  mov    esi,edi
  call   GUS_mem_in
  cmp    al,0AAh
  jnz    @@exit
  add    edi,40000h
  inc    bl
  cmp    bl,4
  jbe    @@fm_loop
@@exit:
  sub    edi,100h
  jz     @@standard
  mov    [dword funk_info.sample_memory_lim],edi
  ret
@@standard:
  mov    [dword funk_info.sample_memory_lim],40000h
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; NB/this 'total reset' routine is from adam's dos32 routines              ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_total_reset
  cmp    [byte init_settings.IRQ_no],0
  je     @@zero_irq1
  movzx  edx,[byte init_settings.IRQ_no]
  mov    dl,[byte edx+GUS_irq_table]
  or     dl,dl
  jz     @@invalid_setting
@@Zero_irq1:
  mov    [byte @@irq_control],dl
  movzx  edx,[byte init_settings.IRQ_no2]
  or     dl,dl
  jz     @@Zero_irq2
  mov    dl,[byte edx+GUS_irq_table]
  or     dl,dl
  jz     @@invalid_setting
  shl    dl,3
@@Zero_irq2:
  or     [byte @@irq_control],dl
  mov    al,[byte init_settings.IRQ_no]
  cmp    al,[byte init_settings.IRQ_no2]
  jne    @@diff_irqs
  cmp    [byte init_settings.IRQ_no],0
  je     @@diff_irqs
  and    [byte @@irq_control],0111b
  or     [byte @@irq_control],40h
@@diff_irqs:
  movzx  edx,[byte init_settings.DMA_no]
  or     dl,dl
  jz     @@Zero_dma2
  mov    dl,[byte edx+GUS_dma_table]
  or     dl,dl
  jz     @@invalid_setting
@@Zero_dma1:
  mov    [byte @@dma_control],dl
  movzx  edx,[byte init_settings.DMA_no2]
  or     dl,dl
  jz     @@Zero_dma2
  mov    dl,[byte edx+GUS_dma_table]
  or     dl,dl
  jz     @@invalid_setting
  shl    dl,3
@@Zero_dma2:
  or     [byte @@dma_control],dl
  mov    al,[byte init_settings.DMA_no]
  cmp    al,[byte init_settings.DMA_no2]
  jne    @@diff_dmas
  and    cl,cl
  jz     @@diff_irqs
  and    [byte @@dma_control],0111b
  or     [byte @@dma_control],40h
@@diff_dmas:
  cli
  mov    ecx,200h
  loop   $
  mov    edx,[dword GUS_base]
  add    edx,0fh
  mov    al,5
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,00001011b
  out    dx,al
  mov    edx,[dword GUS_irqdmacontrol]
  xor    al,al
  out    dx,al
  mov    edx,[dword GUS_base]
  add    edx,0fh
  xor    al,al
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,00001011b
  out    dx,al
  mov    edx,[dword GUS_irqdmacontrol]
  mov    al,[byte @@dma_control]
  or     al,80h
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,01001011b
  out    dx,al
  mov    edx,[dword gus_irqdmacontrol]
  mov    al,[byte @@irq_control]
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,00001011b
  out    dx,al
  mov    edx,[dword gus_irqdmacontrol]
  mov    al,[byte @@dma_control]
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,01001011b
  out    dx,al
  mov    edx,[dword gus_irqdmacontrol]
  mov    al,[byte @@irq_control]
  out    dx,al
  mov    edx,[dword gus_voice]
  xor    al,al
  out    dx,al
  mov    edx,[dword GUS_base]
  mov    al,0001001b
  out    dx,al
  mov    edx,[dword gus_voice]
  xor    al,al
  out    dx,al
  in     al,0A1h
  mov    ah,al
  in     al,21h
  mov    cl,[byte init_settings.IRQ_no]
  cmp    cl,2
  jne    @@j3
  mov    cl,9
@@j3:
  mov    ebx,1
  shl    ebx,cl
  not    ebx
  and    eax,ebx
  mov    cl,[byte init_settings.IRQ_no2]
  cmp    cl,2
  jne    @@j4
  mov    cl,9
@@j4:
  mov    ebx,1
  shl    ebx,cl
  not    ebx
  and    eax,ebx
  out    021h,al
  mov    al,ah
  out    0A1h,al
;part II
  mov    edx,[dword gus_command]
  mov    al,04Ch
  out    dx,al
  add    edx,2
  mov    al,00000000b
  out    dx,al
  mov    ecx,10
@@J56:
  call   gus_delay
  loop   @@J56
  mov    edx,[dword gus_command]
  mov    al,04Ch
  out    dx,al
  add    edx,2
  mov    al,00000001b
  out    dx,al
  mov    ecx,10
@@J57:
  call   gus_delay
  loop   @@J57
  mov    edx,[dword gus_midictrl]
  mov    al,00000011b
  out    dx,al
  mov    ecx,10
@@J58:
  call   gus_delay
  loop   @@J58
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,041h
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,045h
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,049h
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,049h
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,0eh
  out    dx,al
  add    edx,2
  mov    al,13 or 0C0h
  out    dx,al
  mov    edx,[dword gus_GF1_status]
  in     al,dx
  mov    edx,[dword gus_command]
  mov    al,041h
  out    dx,al
  add    edx,2
  in     al,dx
  mov    edx,[dword gus_command]
  mov    al,049h
  out    dx,al
  add    edx,2
  in     al,dx
@@ClrFIFO:
  mov    edx,[dword gus_command]
  mov    al,08fh
  out    dx,al
  add    edx,2
  in     al,dx
  and    al,11000000b
  cmp    al,11000000b
  jne    @@ClrFIFO
  xor    bl,bl
@@stop_loop:
  mov    edx,[dword GUS_voice]
  mov    al,bl
  out    dx,al
  mov    edx,[dword gus_command]
  xor    al,al
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  mov    edx,[dword gus_command]
  xor    al,al
  out    dx,al
  add    edx,2
  mov    al,10b
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,0dh
  out    dx,al
  add    edx,2
  mov    al,10b
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,09h
  out    dx,al
  inc    edx
  movzx  eax,[byte gus_volume_table]
  shl    eax,8
  out    dx,ax
  call   gus_delay
  inc    bl
  cmp    bl,14
  jb     @@stop_loop
  mov    edx,[dword gus_GF1_status]
  in     al,dx
  mov    edx,[dword gus_command]
  mov    al,041h
  out    dx,al
  add    edx,2
  in     al,dx
  mov    edx,[dword gus_command]
  mov    al,049h
  out    dx,al
  add    edx,2
  in     al,dx
@@Clr2FIFO:
  mov    edx,[dword gus_command]
  mov    al,08fh
  out    dx,al
  add    edx,2
  in     al,dx
  and    al,11000000b
  cmp    al,11000000b
  jne    @@Clr2FIFO
  mov    edx,[dword gus_command]
  mov    al,0eh
  out    dx,al
  add    edx,2
  mov    al,13 or 0C0h
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,04ch
  out    dx,al
  add    edx,2
  mov    al,00000111b
  out    dx,al
  sti
@@invalid_setting:
  ret
@@dma_control:
  db ?
@@irq_control:
  db ?
@@gf1:
  db ?
@@midi:
  db ?
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_reset
;added code
  mov    edx,[dword gus_base]
  mov    al,0bh
  out    dx,al
;added code
  mov    ebx,[dword gus_Command]
  mov    ecx,[dword gus_DataHi]
  mov    edx,ebx
  mov    al,4Ch
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  call   gus_delay
  call   gus_delay
  mov    edx,ebx
  mov    al,4Ch
  out    dx,al
  mov    edx,ecx
  mov    al,1
  out    dx,al
  call   gus_delay
  call   gus_delay
  mov    edx,ebx
  mov    al,41h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  mov    edx,ebx
  mov    al,45h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  mov    edx,ebx
  mov    al,49h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  mov    edx,ebx
  mov    al,0Eh
  out    dx,al
  add    edx,2
  mov    al,13
  or     al,0Ch
  out    dx,al
  mov    edx,[dword gus_Status]
  in     al,dx
  mov    edx,ebx
  mov    al,41h
  out    dx,al
  mov    edx,ecx
  in     al,dx
  mov    edx,ebx
  mov    al,49h
  out    dx,al
  mov    edx,ecx
  in     al,dx
  mov    edx,ebx
  mov    al,8Fh
  out    dx,al
  mov    edx,ecx
  in     al,dx
  push   ebx ecx
  xor    ecx,ecx
  mov    bl,0
@@VoiceClearLoop:
  mov    edx,[dword gus_Voice]
  mov    al,cl
  out    dx,al
  inc    edx
  xor    al,al
  out    dx,al
  add    edx,2
  mov    al,3
  out    dx,al
  sub    edx,2
  mov    al,0Dh
  out    dx,al
  add    edx,2
  mov    al,3
  out    dx,al
  sub    edx,2
  mov    al,0ch
  out    dx,al
  add    edx,2
  mov    al,bl
  out    dx,al
  sub    edx,2
  mov    al,09h
  out    dx,al
  inc    edx
  movzx  eax,[byte gus_volume_table]
  shl    eax,8
  out    dx,ax
  xor    bl,0ffh
  inc    ecx
  cmp    ecx,13
  jnz    @@VoiceClearLoop
  pop    ecx ebx
  mov    edx,ebx
  mov    al,41h
  out    dx,al
  mov    edx,ecx
  in     al,dx
  mov    edx,ebx
  mov    al,49h
  out    dx,al
  mov    edx,ecx
  in     al,dx
  mov    edx,ebx
  mov    al,8Fh
  out    dx,al
  mov    edx,ecx
  in     al,dx
  mov    edx,ebx
  mov    al,4Ch
  out    dx,al
  mov    edx,ecx
  mov    al,7
  out    dx,al
;added code
  mov    edx,ebx
  mov    al,4Ch
  out    dx,al
  mov    edx,ecx
  mov    al,7
  out    dx,al
;;;;
  mov    edx,[dword gus_base]                             ;set irq
  mov    al,4bh
  out    dx,al
  movzx  eax,[byte init_settings.IRQ_no]
  mov    al,[byte eax+GUS_irq_table]
  push   eax
  MOV    edx,[dword gus_irqdmacontrol]
  OUT    dx,AL
;;;;
  mov    edx,[dword gus_base]                             ;set dma
  mov    al,0bh
  out    dx,al
  movzx  eax,[byte init_settings.DMA_no]
  mov    al,[byte eax+GUS_dma_table]
  MOV    edx,[dword gus_irqdmacontrol]
  OUT    dx,AL
;;;;
  mov    edx,[dword gus_base]                             ;set irq
  mov    al,4bh
  out    dx,al
  MOV    edx,[dword gus_irqdmacontrol]
  pop    eax
  OUT    dx,AL
;;;;
  mov    edx,[dword gus_base]
  mov    al,0bh
  out    dx,al
  mov    edx,ebx
  mov    al,41h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  mov    edx,ebx
  mov    al,45h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  mov    edx,ebx
  mov    al,49h
  out    dx,al
  mov    edx,ecx
  xor    al,al
  out    dx,al
  MOV    AL,80h
  MOV    edx,[dword gus_timerdata]
  OUT    dx,AL
  xor    al,al
  MOV    edx,[dword gus_timerdata]
  OUT    dx,AL
  mov    edx,[dword gus_base]
  mov    al,0bh
  out    dx,al
;added code
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; GUS Interrupt ready                                                      ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_intready
  cli
  xor    cl,cl
  xor    bl,bl
@@resetb:
  mov    edx,[dword gus_Voice]
  mov    al,cl
  out    dx,al
  mov    edx,[dword gus_command]
  mov    al,0ch
  out    dx,al
  add    edx,2
  mov    al,bl
  out    dx,al
  xor    bl,0ffh
  inc    cl
  cmp    cl,13
  jnz    @@resetb

  mov    edx,[dword gus_command]
  mov    al,45h
  out    dx,al
  mov    al,2bh
  and    al,0f7h
  mov    edx,[dword gus_datahi]
  out    dx,al
  xor    al,al
  out    021h,al
  xor    al,al
  out    0a1h,al
  mov    al,20h
  out    020h,al
  mov    al,20h
  out    0a0h,al
  mov    al,47h
  mov    edx,[dword gus_command]
  out    dx,al
  mov    al,193 ;<<-speed
  add    edx,2
  out    dx,al
  sub    edx,2
  mov    al,45h
  out    dx,al
  add    edx,2
  mov    al,8
  out    dx,al
  mov    edx,[dword gus_timercontrol]
  mov    al,4
  out    dx,al
  mov    edx,[dword gus_timerdata]
  mov    al,2
  out    dx,al
  mov    al,8
  mov    edx,[dword gus_base]
  out    dx,al
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; GUS Save INT timer registers                                             ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_init
  mov    bl,[byte init_settings.IRQ_no]
  call   getirqvector
  mov    [dword DAC_old_irq_ofs],edx
  mov    [word DAC_old_irq_sel],cx

  mov    bl,[byte init_settings.IRQ_no]
  lea    edx,[GUS_mixxer]
  mov    ecx,cs
  call   setirqvector
  call   GUS_total_reset
  call   GUS_reset
  call   GUS_intready
  ret
endp

proc GUS_deinit
  call   GUS_reset
  mov    bl,[byte init_settings.IRQ_no]
  mov    edx,[dword DAC_old_irq_ofs]
  mov    cx,[word DAC_old_irq_sel]
  call   setirqvector
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   esi     trans mem                                                      ;
;   edi     gus mem locatin                                                ;
;   ecx     bytes to trans                                                 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc gus_upload
  cli
  mov    edx,[dword gus_Command]
@@MainLoop:
  mov    edx,[dword gus_Command]
  mov    al,44h
  out    dx,al
  add    edx,2
  mov    eax,edi
  shr    eax,16
  out    dx,al
  mov    edx,[dword gus_Command]
  mov    al,43h
  out    dx,al
  inc    edx
  mov    eax,edi
  out    dx,ax
  mov    edx,[dword gus_dram]
  outsb
  inc    edi
  dec    ecx
  jnz    @@MainLoop
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;   edi     trans mem                                                      ;
;   esi     gus mem locatin                                                ;
;   ecx     bytes to trans                                                 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc gus_dnload
  cli
  mov    edx,[dword gus_Command]
@@MainLoop:
  mov    edx,[dword gus_Command]
  mov    al,44h
  out    dx,al
  add    edx,2
  mov    eax,esi
  shr    eax,16
  out    dx,al
  mov    edx,[dword gus_Command]
  mov    al,43h
  out    dx,al
  inc    edx
  mov    eax,esi
  out    dx,ax
  mov    edx,[dword gus_dram]
  insb
  inc    esi
  dec    ecx
  jnz    @@MainLoop
  sti
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; PAS DEPENDANT CODE                                                       ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc PAS16_virtualmixxer
  cld
  push   ds es
  pushad
  mov    ax,DGROUP
  mov    ds,ax
  mov    es,ax
@@ISR_l:
  mov    edx,INTRCTLRST                                 ;clear the interrupt
  xor    edx,[_MVTranslateCode]                         ;xlate the board address
  in     al,dx
  test   al,bISsampbuff+bISsamprate                     ;our interrupt?
  jz     @@exit_int
  out    dx,al                                          ;yes, flush it...
  mov    esi,[dword DAC_mix_buffer]
  mov    edx,[dword DAC_mix_buffer_right]
  mov    edi,[dword DAC_mix_buffer2]
  add    edi,[DAC_double_buf1]
  mov    eax,[DAC_double_buf1]
  xchg   eax,[DAC_double_buf2]
  mov    [DAC_double_buf1],eax
  mov    ecx,[dword DAC_mix_buffer_size]
  xor    ebx,ebx
@@trans16:
  mov    eax,[edx+ebx]
  shl    eax,16
  mov    ax,[word esi+ebx]
  mov    [edi+ebx*2],eax
  add    ebx,2
  dec    ecx
  jnz    @@trans16
  call   funk_tracker
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer]
  xor    eax,eax
  rep    stosd
  mov    ecx,[dword DAC_mix_buffer_size]
  shr    ecx,1
  mov    edi,[dword DAC_mix_buffer_right]
  rep    stosd
  lea    ebx,[funk_chan1]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan2]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan3]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan4]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan5]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan6]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan7]
  mov    edi,[dword DAC_mix_buffer]
  call   DAC_channel_mixxer
  lea    ebx,[funk_chan8]
  mov    edi,[dword DAC_mix_buffer_right]
  call   DAC_channel_mixxer
  jmp    @@ISR_l
@@exit_int:
  mov    al,20h                                         ;clear the interrupt
  cmp    [init_settings.IRQ_no],8                       ;2nd IRQ controller?
  jb     @@F1
  out    0A0h,al
@@F1:
  out    020h,al
  popad
  pop    es ds
  iretd
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Pro Audio routines for FunkTracker                                      ;
;                                                                         ;
; Routines from Media Vision SDK, Inc.                                    ;
; Copyright (c) 1991,1992. All Rights Res.                                ;
;                                                                         ;
; Converted to P-mode by Jason Nunn                                       ;
;                                                                         ;
; This INIT code that talks to MVSOUND.SYS isn't supposed to run in P-mode;
; So, problems _may_ occur. I'll replace it when i get time.              ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Hardware associated with the PAS1/PAS2/CDPC
DEFAULT_BASE          = 00388h  ;default base I/O address
ALT_BASE_1            = 00384h  ;first alternate address
ALT_BASE_2            = 0038Ch  ;second alternate address
ALT_BASE_3            = 00288h  ;third alternate address
USE_ACTIVE_ADDR       = 00000h  ;uses what is currently active
AUDIOFILT             = 00B8Ah  ;Audio Filter Control Register
INTRCTLRST            = 00B89h  ;Interrupt Control Status Register
INTRCTLR              = 00B8Bh  ;Interrupt Control Register write
CROSSCHANNEL          = 00F8Ah  ;Cross Channel Register
SAMPLERATE            = 01388h  ;(t0) Sample Rate Timer Register
SAMPLECNT             = 01389h  ;(t1) Sample Count Register
TMRCTLR               = 0138Bh  ;Local Timer Control Register
; Factory Default Settings
DEFAULTDMA            = 1       ;DMA channel 1
DEFAULTIRQ            = 7       ;IRQ channel 7
DEFAULTINT            = 65h     ;Interrupt # for software interface
; mixer select
OUTPUTMIXER           = 00h     ;output mixer H/W select
INPUTMIXER            = 40h     ;input mixer select
DEFMIXER              = -1      ;use last mixer selected
MIXERMAX              = 1fh     ;maximum mixer setting
MVVOLUMEMAX           = 3fh     ;MVA508 maximum mixer setting
NSVOLUMEMAX           = 28h     ;National maximum mixer setting
EQUALIZERMAX          = 0ch     ;maximum equalizer setting
EQUALIZERMID          = 06h     ;maximum mid setting
; Filter register bits
fFIdatabits           = 00011111B ;filter select and decode field bits
fFImutebits           = 00100000B ;filter mute field bits
fFIpcmbits            = 11000000B ;filter sample rate field bits
bFImute               = 00100000B ;filter mute bit
bFIsrate              = 01000000B ;filter sample rate timer mask
bFIsbuff              = 10000000B ;filter sample buffer counter mask
FILTERMAX             = 6         ;six possible settings
; Cross Channel Bit definitions
fCCcrossbits          = 00001111B ;; cross channel bit field
fCCpcmbits            = 11110000B ;; pcm/dma control bit field
bCCr2r                = 00000001B ;; CROSSCHANNEL Right to Right
bCCl2r                = 00000010B ;; CROSSCHANNEL Left  to Right
bCCr2l                = 00000100B ;; CROSSCHANNEL Right to Right
bCCl2l                = 00001000B ;; CROSSCHANNEL Left  to Left
bCCdac                = 00010000B ;; DAC/ADC Control
bCCmono               = 00100000B ;; PCM Monaural Enable
bCCenapcm             = 01000000B ;; Enable PCM state machine
bCCdrq                = 10000000B ;; Enable DRQ bit
; Interrupt Control Register Bits
fICintmaskbits        = 00011111B ;; interrupt mask field bits
fICrevbits            = 11100000B ;; revision mask field bits
bICsamprate           = 00000100B ;; Sample Rate timer interrupt enable
bICsampbuff           = 00001000B ;; Sample buffer timer interrupt enable
fICrevshr             = 5         ;; rotate rev bits to lsb


;Interrupt Status Register Bits

fISints               = 1fh ; Interrupt bit field
bISleftfm             = 01h ; Left FM interrupt active
bISritfm              = 02h ; Right FM interrupt active
bISsamprate           = 04h ; Sample Rate timer interrupt active
bISsampbuff           = 08h ; Sample buffer timer interrupt active
bISmidi               = 10h ; MIDI interrupt active
bISPCMlr              = 20h ; PCM left/right active
bISActive             = 40h ; Hardware is active (not in reset)
bISClip               =	80h ; Sample Clipping has occured

MASTERCHIPR           = 0ff88h  ;; Master Chip Rev      (r)
ENHANCEDSCSI          = 07f89h  ;; Enhanced SCSI detect port
SYSCONFIG2            = 08389h  ;; System Config 2      (r/w)
COMPATREGE            = 0f788h  ;; Compatible Rgister Enable (r/w)
MASTERMODRD           = 0ff8bh  ;; Master Mode Read      (r)
SLAVEMODRD            = 0ef8bh  ;; Slave Mode Read      (r)
; Sys Config 2
bSC216bit             = 00000100b ;; 16 bit audio
bSC212bit             = 00001000b ;; 12 bit interleaving (d2 must be set too)
; I/O Config 3
bIC3pcmint            = 00001111b       ;; pcm IRQ channel select
bIC3cdint             = 11110000b ;; cd  IRQ channel select
; Compatibility Register
cpMPUEmulation        = 00000001b ;; MPU emuation is on bit
; Slave Mode Read
bSMRDdrvtyp           = 00000011b       ;; drive interface type
bSMRDfmtyp            = 00000100b ;; FM chip type
bSMRDdactyp           = 00001000b ;; 16 bit dac (1) or 8 bit dac (0)
; Master Mode Read
bMMRDatps2            = 00000001b       ;; AT(1) or PS2(0) bus
bMMRDmsmd             = 00000100b ;; master/slave mode
; Interrupt Controller #1 Port Addresses and Interrupt Masks
IRQ1MASKREG           = 021h  ;; 8259 mask register
INT7MSK               = 10000000B ;; interrupt 7 mask
; Interrupt Controller #2 Port Addresses and Interrupt Masks
IRQ2MASKREG           = 0A1h  ;; 8259 mask register
;; dma controller #1 port addresses
DMAC0ADDR             = 000h  ;; DMA channel 0 Base & Current Address
DMAC0COUNT            = 001h  ;; DMA channel 0 Base & Current Count
DMAC1ADDR             = 002h  ;; DMA channel 1 Base & Current Address
DMAC1COUNT            = 003h  ;; DMA channel 1 Base & Current Count
DMAC2ADDR             = 004h  ;; DMA channel 2 Base & Current Address
DMAC2COUNT            = 005h  ;; DMA channel 2 Base & Current Count
DMAC3ADDR             = 006h  ;; DMA channel 3 Base & Current Address
DMAC3COUNT            = 007h  ;; DMA channel 3 Base & Current Count
DMA2C4ADDR            = 0C0h  ;; DMA channel 4 Base & Current Address
DMA2C4COUNT           = 0C2h  ;; DMA channel 4 Base & Current Count
DMA2C5ADDR            = 0C4h  ;; DMA channel 5 Base & Current Address
DMA2C5COUNT           = 0C6h  ;; DMA channel 5 Base & Current Count
DMA2C6ADDR            = 0C8h  ;; DMA channel 6 Base & Current Address
DMA2C6COUNT           = 0CAh  ;; DMA channel 6 Base & Current Count
DMA2C7ADDR            = 0CCh  ;; DMA channel 7 Base & Current Address
DMA2C7COUNT           = 0CEh  ;; DMA channel 7 Base & Current Count
DMARDSTAT             = 008h  ;; DMA read status
DMAWRCNTRL            = 008h  ;; DMA write command register
DMAWREQ               = 009h  ;; DMA write request register
DMAWRSMR              = 00Ah  ;; DMA write single mask register
DMAWRMODE             = 00Bh  ;; DMA write mode register
DMACLEAR              = 00Ch  ;; DMA clear low/high flip-flop
DMARDTEMP             = 00Dh  ;; DMA read temp register
DMAWRCLR              = 00Dh  ;; DMA write master clear
DMACLRMSK             = 00Eh  ;; DMA clear mask register
DMAWRALL              = 00Fh  ;; DMA write all mask register bits
DMA2RDSTAT            = 0D0h  ;; DMA read status
DMA2WRCNTRL           = 0D0h  ;; DMA write command register
DMA2WREQ              = 0D2h  ;; DMA write r=est register
DMA2WRSMR             = 0D4h  ;; DMA write single mask register
DMA2WRMODE            = 0D6h  ;; DMA write mode register
DMA2CLEAR             = 0D8h  ;; DMA clear low/high flip-flop
DMA2RDTEMP            = 0DAh  ;; DMA read temp register
DMA2WRCLR             = 0DAh  ;; DMA write master clear
DMA2CLRMSK            = 0DCh  ;; DMA clear mask register
DMA2WRALL             = 0DEh  ;; DMA write all mask register bits
CH0PAGEREG            = 087h  ;; Channel 0 Page Register
CH1PAGEREG            = 083h  ;; Channel 1 Page Register
CH2PAGEREG            = 081h  ;; Channel 2 Page Register
CH3PAGEREG            = 082h  ;; Channel 3 Page Register
CH5PAGEREG            = 08Bh  ;; Channel 5 Page Register
CH6PAGEREG            = 089h  ;; Channel 6 Page Register
CH7PAGEREG            = 08Ah  ;; Channel 7 Page Register

struc MVState
  _sysspkrtmr         db 0 ;    42 System Speaker Timer Address
  _systmrctlr         db 0 ;    43 System Timer Control
  _sysspkrreg         db 0 ;    61 System Speaker Register
  _joystick           db 0 ;   201 Joystick Register
  _lfmaddr            db 0 ;   388 Left  FM Synth Address
  _lfmdata            db 0 ;   389 Left  FM Synth Data
  _rfmaddr            db 0 ;   38A Right FM Synth Address
  _rfmdata            db 0 ;   38B Right FM Synth Data
  _dfmaddr            db 0 ;   788 Dual  FM Synthesizer Address Register
  _dfmdata            db 0 ;   789 Dual  FM Synthesizer Data Register
                      db 0 ; reserved for future use
  _paudiomixr         db 0 ;   78B Paralllel Audio Mixer Control
  _audiomixr          db 0 ;   B88 Audio Mixer Control
  _intrctlrst         db 0 ;   B89 Interrupt Status
  _audiofilt          db 0 ;   B8A Audio Filter Control
  _intrctlr           db 0 ;   B8B Interrupt Control
  _pcmdata            db 0 ;   F88 PCM Data I/O Register
  _RESRVD2            db 0 ; reserved
  _crosschannel       db 0 ;   F8A Cross Channel
  _RESRVD3            db 0 ; reserved
  _samplerate         dw 0 ;  1388 Sample Rate Timer
  _samplecnt          dw 0 ;  1389 Sample Count Register
  _spkrtmr            dw 0 ;  138A Shadow Speaker Timer Count
  _tmrctlr            db 0 ;  138B Shadow Speaker Timer Control
  _mdirqvect          db 0 ;  1788 MIDI IRQ Vector Register
  _mdsysctlr          db 0 ;  1789 MIDI System Control Register
  _mdsysstat          db 0 ;  178A MIDI IRQ Status Register
  _mdirqclr           db 0 ;  178B MIDI IRQ Clear Register
  _mdgroup1           db 0 ;  1B88 MIDI Group #1 Register
  _mdgroup2           db 0 ;  1B89 MIDI Group #2 Register
  _mdgroup3           db 0 ;  1B8A MIDI Group #3 Register
  _mdgroup4           db 0 ;  1B8B MIDI Group #4 Register
ends

bMVA508               = 0000000000000001b ; MVA508(1) or National(0)
bMVPS2                = 0000000000000010b ; PS2 bus stuff
bMVSLAVE              = 0000000000000100b ; CDPC Slave device is present
bMVSCSI               = 0000000000001000b ; SCSI interface
bMVENHSCSI            = 0000000000010000b ; Enhanced SCSI interface
bMVSONY               = 0000000000100000b ; Sony 535 interface
bMVDAC16              = 0000000001000000b ; 16 bit DAC
bMVSBEMUL             = 0000000010000000b ; SB h/w emulation
bMVMPUEMUL            = 0000000100000000b ; MPU h/w emulation
bMVOPL3               = 0000001000000000b ; OPL3(1) or 3812(0)
bMV101                = 0000010000000000b ; MV101 ASIC
bMV101_REV            = 0111100000000000b ; MV101 Revision
bMV101_MORE           = 1000000000000000b ; more bits in BX
;; Define the ASIC versions
ASIC_VERSION_B        = 0000000000000010b ; revision B
ASIC_VERSION_C        = 0000000000000011b ; revision C
ASIC_VERSION_D        = 0000000000000100b ; revision D
ASIC_VERSION_E        = 0000000000000101b ; revision E
ASIC_VERSION_F        = 0000000000000110b ; revision F
;; First Pro Audio Spectrum feature list
PRODUCT_PROAUDIO      = bMVSCSI
;; Pro Audio Plus feature list
PRODUCT_PROPLUS       = bMV101+bMVSCSI+bMVENHSCSI+bMVSBEMUL+bMVOPL3
;; Pro Audio Spectrum 16 feature list
PRODUCT_PRO16         = bMV101+bMVA508+bMVSCSI+bMVENHSCSI+bMVSBEMUL+bMVDAC16+bMVOPL3
;; CDPC feature list
PRODUCT_CDPC          = bMV101+bMVSLAVE+bMVSONY+bMVSBEMUL+bMVDAC16+bMVOPL3
;; Set each one to zero - to be init later if selected
PROAS100              = 0
PROAS200              = 0
PROAS300              = 0
CDPC                  = 0
PRODUCTDEFINED        = 0                 ; to be set if a product is selected
_MVTranslateCode      dd 0                ; I/O base xor default_base
_MVHWVersionBits      dw -1               ; holds the product feature bits
VERSION_PAS           = 0                 ; Pro Audio Spectrum
VERSION_PASPLUS       = 1                 ; Pro Audio Plus card
VERSION_PAS16         = 2                 ; Pro Audio 16 card
VERSION_CDPC          = 3                 ; CDPC card & unit
; The following equates build up a mask of bits that we do wish to keep
; when comparing feature bits. The zero bits can be ignored, whereas, the
; the 1 bits must match.
PASdocare = bMVA508 OR bMVDAC16 OR bMVOPL3 OR bMV101
PASPLUSdocare = bMVA508 OR bMVDAC16 OR bMVOPL3 OR bMV101
PAS16docare = bMVA508 OR bMVDAC16 OR bMVOPL3 OR bMV101
CDPCdocare = bMVA508 OR bMVDAC16 OR bMVOPL3 OR bMV101
label MVProductIDTable
  dw PRODUCT_PROAUDIO and PASdocare
  dw PRODUCT_PROPLUS  and PASPLUSdocare
  dw PRODUCT_PRO16  and PAS16docare
  dw PRODUCT_CDPC  and CDPCdocare
  dw -1
label MVDoCareBits
  dw PASdocare
  dw PASPLUSdocare
  dw PAS16docare
  dw CDPCdocare
  dw -1

struc dmaaddr
  _dmach              db ? ; DMA channel selected
  _dmardstat          db ? ; DMA read status
  _dmawrcntrl         db ? ; DMA write command register
  _dmawreq            db ? ; DMA write request register
  _dmawrsmr           db ? ; DMA write single mask register
  _dmawrmode          db ? ; DMA write mode register
  _dmaclear           db ? ; DMA clear low/high flip-flop
  _dmardtemp          db ? ; DMA read temp register
  _dmawrclr           db ? ; DMA write master clear
  _dmaclrmsk          db ? ; DMA clear mask register
  _dmawrall           db ? ; DMA write all mask register bits
ends

MVOurDMAPageReg       dw CH1PAGEREG  ; default to DMA channel 1 page reg
MVOurDMAddress        dw DMAC1ADDR   ; default to DMA channel 1 address reg
label DMA1AddrTable
  db DEFAULTDMA ; DMA channel selected
  db DMARDSTAT  ; DMA read status
  db DMAWRCNTRL ; DMA write command register
  db DMAWREQ    ; DMA write request register
  db DMAWRSMR   ; DMA write single mask register
  db DMAWRMODE  ; DMA write mode register
  db DMACLEAR   ; DMA clear low/high flip-flop
  db DMARDTEMP  ; DMA read temp register
  db DMAWRCLR   ; DMA write master clear
  db DMACLRMSK  ; DMA clear mask register
  db DMAWRALL   ; DMA write all mask register bits
; table of address pointers to the various DMA 2 addresses
label DMA2AddrTable
  db DEFAULTDMA ; DMA channel selected
  db DMA2RDSTAT ; DMA read status
  db DMA2WRCNTRL; DMA write command register
  db DMA2WREQ   ; DMA write request register
  db DMA2WRSMR  ; DMA write single mask register
  db DMA2WRMODE ; DMA write mode register
  db DMA2CLEAR  ; DMA clear low/high flip-flop
  db DMA2RDTEMP ; DMA read temp register
  db DMA2WRCLR  ; DMA write master clear
  db DMA2CLRMSK ; DMA clear mask register
  db DMA2WRALL  ; DMA write all mask register bits
PCM_DMAPointer        dd offset DMA1AddrTable           ;default to channel 1 table
MVTheIRQMask          db INT7MSK                        ;8259 interrupt mask
MVSampleSize          db 2                              ;sample size: 0=8,1=12,2=16
mvhwShadowPointer     dd 0                              ;points to the start of the data table
MVHardwareShadowTable MVState <>
MVTypeOfSetup         db 0                              ;polled/dma interrupt masks & stuff..
mvStatusWord          dw 0                              ;Holds current status:
                                                        ; 0 = inactive, available
                                                        ; 1 = currently playing
                                                        ; 2 = currently recording
DMAMASK               = bICsampbuff                     ;dma mask
POLLEDMASK            = bICsamprate+bICsampbuff         ;polled mask

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc PCM_pause
  jmp    @@j
@@j:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Given a specific I/O address, this routine will see if the              ;
; hardware exists at this address.                                        ;
;                                                                         ;
; Entry Conditions:                                                       ;
;     DI holds the I/O address to test                                    ;
;     BX:CX = bMVSCSI                                                     ;
;                                                                         ;
; Exit Conditions:                                                        ;
;     BX:CX = the bit fields that identify the board                      ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SearchHWVersion
; calculate the translation code
  xor    edi,DEFAULT_BASE
  mov    eax,0BC00H                                      ;make sure MVSOUND.SYS is loaded
  mov    ebx,'??'                                        ;this is our way of knowing if the
  xor    ecx,ecx                                        ;hardware is actually present.
  xor    edx,edx
  int    2fh                                            ;get the ID pattern
  xor    bx,cx                                          ;build the result
  xor    bx,dx
  cmp    bx,'MV'                                        ;if not here, exit...
  jnz    @@sehw_bad
; get the MVSOUND.SYS specified DMA and IRQ channel
  mov    eax,0bc04h                                     ;get the DMA and IRQ numbers
  int    2fh
  mov    [init_settings.DMA_no],bl                             ;save the correct DMA & IRQ
  mov    [init_settings.IRQ_no],cl
; grab the version # in the interrupt mask. The top few bits hold the version #
  mov    edx,INTRCTLR                                    ;board ID is in MSB 3 bits
  xor    edx,edi
  in     al,dx
  cmp    al,-1                                          ;bus float meaning not present?
  je     @@sehw_bad                                     ;yes, there is no card here
  mov    ah,al
  xor    al,fICrevbits
  out    dx,al
  call   PCM_pause
  call   PCM_pause
  in     al,dx
  cmp    al,ah
  xchg   al,ah
  out    dx,al
  jnz    @@sehw_bad                                     ;we have a bogus board
  and    ax,fICrevbits                                  ;isolate the ID bits & clear AH
  mov    cl,fICrevshr                                   ;shift the bits into a meaningful
  shr    al,cl                                          ;position (least signficant bits)
  movzx  esi,ax                                         ;save the version #
; We do have hardware! Load the product bit definitions
  sub    ebx,ebx
  mov    ecx,bMVSCSI                                    ;setup bx:cx for the original PAS
  or     al,al                                          ;is this the first version of h/w?
  jz     @@sehw_done                                    ;yes, simple exit will do.
; Checks the installed hardware for all the feature bits.                 ;
; All second generation Pro Audio cards use the MV101 and have SB emulation.
  or     ecx,bMVSBEMUL+bMV101                            ;force SB emulation
; determine if the enhanced SCSI interface is present
  mov    edx,ENHANCEDSCSI                                ;test for SCSI mod (U48)
  xor    edx,edi                                          ;modify via the translate code
  out    dx,al                                          ;strobe
  call   PCM_pause                                      ;I/O bus delay
  in     al,dx                                          ;get the bit
  and    al,1                                           ; bit0==1 means old SCSI PAL
  cmp    al,1                                           ;reverse sense
  sbb    eax,eax                                         ;ax = ffff if enhanced SCSI
  and    eax,bMVENHSCSI
  or     ecx,eax
; determine AT/PS2, CDPC slave mode
  mov    edx,MASTERMODRD
  xor    edx,edi
  in     al,dx
  test   al,bMMRDatps2                                  ;AT(1) or PS2(0)
  jnz    @@F1
  or     ecx,bMVPS2
@@F1:
  test   al,bMMRDmsmd                                   ;Master(0) or Slave(1)
  jz     @@F2
  or     ecx,bMVSLAVE
@@f2:
  push   ecx
  mov    edx,MASTERCHIPR
  xor    edx,edi
  in     al,dx
  and    ax,000Fh
  mov    cl,11
  shl    eax,cl
  pop    ecx
  or     ecx,eax
; determine the CDROM drive type, FM chip, 8/16 bit DAC, and mixer
  mov    edx,SLAVEMODRD
  xor    edx,edi
  in     al,dx
  test   al,bSMRDdactyp                                 ;16 bit DAC?
  jz     @@F3                                           ;no, its an 8 bit DAC
  or     ecx,bMVDAC16                                    ;its a 16 bit DAC
@@f3:
  test   al,bSMRDfmtyp                                  ;OPL3 chip?
  jz     @@F4                                           ;no, so it's the PAS16 card
  or     ecx,bMVOPL3                                     ;is an OPL3
@@f4:
  mov    edx,ecx                                          ;inference check for new mixer
  and    edx,bMVSLAVE+bMVDAC16                           ;Slave & 16 bit dac is the CDPC
  cmp    edx,bMVDAC16                                    ;16 bit DAC on master?
  jnz    @@F5                                           ;no, it's the CDPC with Nation mixer
  or     ecx,bMVA508
@@f5:
  and    al,bSMRDdrvtyp                                 ;isolate the CDROM drive type
  cmp    al,2                                           ;Sony 535 interface?
  jnz    @@F6                                           ;no, continue on...
  and    ecx,NOT (bMVSCSI+bMVENHSCSI)                    ;yes, flush the SCSI bits
  or     ecx,bMVSONY                                     ;set the 535 bit
@@f6:
; determine if MPU-401 emulation is active
  mov    edx,COMPATREGE                                  ;compatibility register
  xor    edx,edi
  in     al,dx
  test   al,cpMPUEmulation
  jz     @@sehw_done
  or     ecx,bMVMPUEMUL
@@sehw_done:
; loop on a table search to find identify the board
  push   ebx
  mov    ebx,-2
@@sehw_05:
  add    ebx,2
  cmp    [word ebx+MVProductIDTable],-1
  jz     @@sehw_bad_hw                                  ;yes, we can't identify this board
  mov    edx,ecx
  and    dx,[word ebx+MVDoCareBits]
  cmp    dx,[word ebx+MVProductIDTable]
  jnz    @@sehw_05
  mov    edx,ebx
  shr    edx,1
  pop    ebx
  movzx  edi,di
  mov    [dword _MVTranslateCode],edi
  mov    eax,esi
  sub    ah,ah                                          ;for our purposes, we will return SCSI
  xchg   ah,al                                          ;into ah
  clc                                                   ;The board was identified !.
  mov    [_mvHWVersionBits],cx
  jmp    @@sehw_exit
@@sehw_bad_hw:
  pop    ebx
  stc
  jmp    @@sehw_exit
@@sehw_bad:
  stc
@@sehw_exit:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Detects and identifies the installed Pro AudioSuctrum.                  ;
;                                                                         ;
; Entry Conditions:                                                       ;
;     edi = word address containing the base address.                     ;
;                                                                         ;
; Exit Conditions:                                                        ;
;     Carry is set on error                                               ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc mvGetHWVersion
  lea    eax,[DACi_freq_convert]
  mov    [dword CARD_freq_convert],eax
  lea    eax,[DACi_volume_convert]
  mov    [dword CARD_volume_convert],eax

  or     edi,edi
  jnz    @@mvgehw_05
  mov    edi,DEFAULT_BASE
  call   SearchHWVersion
  jnc    @@mvgehw_exit
  mov    edi,ALT_BASE_1
  call   SearchHWVersion
  jnc    @@mvgehw_exit
  mov    edi,ALT_BASE_2
  call SearchHWVersion
  jnc @@mvgehw_exit
  mov    edi,ALT_BASE_3
@@mvgehw_05:
  call   SearchHWVersion
@@mvgehw_exit:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Initializes the state table pointer.                                    ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc MVInitStatePtr
; setup a pointer to our local hardware state table
  lea    ebx,[MVHardwareShadowTable]
  mov    [dword mvhwShadowPointer],ebx
  mov    [ebx+MVState._audiomixr],31h                   ;lowest filter setting
  mov    [ebx+MVState._crosschannel],09h                ;cross channel l-2-l, r-2-r
; find the int 2F interface and if found, use it's state table pointer
  mov    ax,0BC00h                                      ;MVSOUND.SYS ID check
  mov    bx,'??'
  sub    cx,cx
  sub    dx,dx
  int    2fh
  xor    bx,cx
  xor    bx,dx
  cmp    bx,'MV'
  jnz    @@imvsp_done                                   ;no, exit home
  mov    ax,0BC02H                                      ;get the pointer
  int    2fh
  cmp    ax,'MV'
  jnz    @@imvsp_done
  movzx  edx,dx
  movzx  ebx,bx
  shl    edx,4
  add    edx,ebx
  push   edx
  mov    ax,0EE02h
  int    31h            ; returns EBX with program base linear address
  pop    edx
  sub    edx,ebx
  mov    [dword mvhwShadowPointer],edx
@@imvsp_done:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Turn off the PCM timers, interrupts, and state machine.                 ;
;                                                                         ;
; Entry Conditions:                                                       ;
;                                                                         ;
; Exit  Conditions:                                                       ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc StopPCM
  push   edi
  mov    edi,[mvhwShadowPointer]
; clear the audio filter sample bits
  mov    edx,AUDIOFILT
  xor    edx,[_MVTranslateCode]
  cli
  mov    al,[edi+MVState._audiofilt]                  ;get the state
  and    al,not (bFIsrate+bFIsbuff)                     ;flush the sample timer bits
  mov    [edi+MVState._audiofilt],al                  ;save the new state
  out    dx,al
; clear the PCM enable bit
  mov    al,[edi+MVState._crosschannel]               ;get the current cross channel
  and    al,not bCCenapcm                               ;clear the PCM enable bit
  or     al,bCCdac
  mov    edx,CROSSCHANNEL
  xor    edx,[_MVTranslateCode]
  out    dx,al                                          ; end to the hardware
  mov    [edi+MVState._crosschannel],al
; disable the 16 bit stuff
  test   [_MVHWVersionBits],bMV101                      ;101 chip?
  jz     @@stpc02                                       ;no, don't touch this...
  mov    edx,SYSCONFIG2
  xor    edx,[_MVTranslateCode]
  in     al,dx
  and    al,not bSC216bit+bSC212bit                     ;flush the 16 bit stuff
  out    dx,al
@@stpc02:
; clear the appropriate Interrupt Control Register bit
  mov    ah,[MVTypeOfSetup]
  and    ah,bICsamprate+bICsampbuff
  not    ah
  mov    edx,INTRCTLR
  xor    edx,[_MVTranslateCode]
  in     al,dx
  and    al,ah                                          ;kill sample timer interrupts
  out    dx,al
  mov    [edi+MVState._intrctlr],al
; clear the system interrupt mask only if no other ints are used
  test   al,fICintmaskbits XOR (bICsamprate+bICsampbuff)
  jnz    @@stpc10
; select the correct IRQ controller, then mask it...
  cmp    [init_settings.IRQ_no],2                              ;Chained IRQ channel?
  jz     @@stpc10                                       ;yes, leave it open...
  mov    dx,IRQ1MASKREG
  cmp    [init_settings.IRQ_no],8                              ;2nd IRQ controller?
  jl     @@stpc05
  mov    dl,IRQ2MASKREG
@@stpc05:
  in     al,dx
  or     al,[MVTheIRQMask]
  out    dx,al
  sti
@@stpc10:
  push   edi
  cmp    [mvStatusWord],0                               ;is there any activity?
  jz     @@kidm_none
  mov    edi,[PCM_DMAPointer]                                ;get the DMA pointer
  sub    dx,dx                                          ;clear out the high byte
; mask out the DMA to stop it
  cli
  mov    al,[edi+dmaaddr._dmach]                         ;get the adjusted dma channel #
  or     al,0100b                                       ;disable the DMA
  mov    dl,[edi+dmaaddr._dmawrsmr]
  out    dx,al
; remove control on the DRQ line
  mov    edi,[mvhwShadowPointer]
  mov    al,[edi+mvstate._crosschannel]               ;get the state
  mov    edx,CROSSCHANNEL
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  and    al,not bCCdrq                                  ;clear the DRQ bit
  out    dx,al
  mov    [edi+mvstate._crosschannel],al               ;and save the new state
  sti
@@kidm_none:
  pop    edi
  mov    [mvStatusWord],0
  pop    edi
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Initializes the PCM code                                                ;
;                                                                         ;
; Entry Conditions:                                                       ;
;     None                                                                ;
;                                                                         ;
; Exit Conditions:                                                        ;
;     AX =  Version of this Code                                          ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc InitPCM
  mov    [MVTypeOfSetup],POLLEDMASK                     ;flushes both interrupts
                                                        ;for upcoming calls
  movsx  eax,[byte init_settings.DMA_no]                  ;initialize the DMA now...
  and    eax,0111b
  mov    ebx,eax
  shl    ebx,1
  jnz    @@seldma_02
  push   sp
  pop    cx
  cmp    cx,sp
  jnz    @@seldma_bad
@@seldma_02:
  mov    dx,[word ebx+@@dmatable]
  or     dx,dx
  jz     @@seldma_bad
  mov    [init_settings.DMA_no],al
  mov    [byte MVOurDMAPageReg],dh
  mov    [byte MVOurDMAddress],dl
  lea    ebx,[DMA1AddrTable]
  cmp    al,4
  jl     @@seldma_05
  lea    ebx,[DMA2AddrTable]
  sub    al,4
@@seldma_05:
  mov    [ebx+dmaaddr._dmach],al
  mov    [dword PCM_DMAPointer],ebx
  sub    ax,ax
@@seldma_bad:
  mov    eax,[dword DAC_old_irq_ofs]
  or     eax,eax
  jz     @@F1
  mov    bl,[init_settings.IRQ_no]
  mov    edx,[dword DAC_old_irq_ofs]
  mov    cx,[word DAC_old_irq_sel]
  call   setirqvector
  mov    [dword DAC_old_irq_ofs],0
  mov    [word DAC_old_irq_sel],0
@@F1:

  sub    ax,ax
  movsx  ecx,[init_settings.IRQ_no]            ;initialize the IRQ now...                                 ;get the irq # (2-7,10-12,14-15)
  and    cl,0fh                                ;save only the valid bits
  mov    bx,01
  shl    bx,cl
  and    bx,1001110010111100b
  jz     @@seirqbad
  mov    [init_settings.IRQ_no],cl
  cmp    cl,8                                  ;2nd interrupt controller?
  jb     @@F3                                  ;no, skip
  xchg   bh,bl
@@f3:
  mov    [mvTheIRQMask],bl

  mov    eax,[DAC_old_irq_ofs]
  or     eax,eax
  jnz    @@F2

  mov    bl,[init_settings.IRQ_no]
  call   getirqvector
  mov    [dword DAC_old_irq_ofs],edx
  mov    [word DAC_old_irq_sel],cx
@@F2:
  mov    ax,1
@@seirqbad:
  dec    ax
  call   StopPCM                                        ;kill the Audio Spectrum board
  mov    edx,INTRCTLRST                                  ;flush any pending PCM irq
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  out    dx,al
  ret
label @@dmatable
  dw     (CH0PAGEREG SHL 8) + DMAC0ADDR
  dw     (CH1PAGEREG SHL 8) + DMAC1ADDR
  dw     (CH2PAGEREG SHL 8) + DMAC2ADDR
  dw     (CH3PAGEREG SHL 8) + DMAC3ADDR
  dw     0
  dw     (CH5PAGEREG SHL 8) + DMA2C5ADDR
  dw     (CH6PAGEREG SHL 8) + DMA2C6ADDR
  dw     (CH7PAGEREG SHL 8) + DMA2C7ADDR
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; LoadDMA  -- Load the DMA controller to read/write data to the MV board
;
; Entry Conditions:
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc LoadDMA
  mov    edi,[mvhwShadowPointer]
  mov    esi,[PCM_DMAPointer]                            ;point to the DMA controller table
  sub    dx,dx                                          ;clear out the high byte
; kill the dma until all programming is done
  mov    al,[esi+dmaaddr._dmach]                         ;get the adjusted dma channel #
  or     al,0100b                                       ;causes all DMA to be suspended
  mov    dl,[esi+dmaaddr._dmawrsmr]
  out    dx,al
; program the mode
  mov    al,01011000b                   ;01011000b       ;get the app's desired mode
  or     al,[esi+dmaaddr._dmach]                         ;merge the adjusted dma channel #
  mov    dl,[esi+dmaaddr._dmawrmode]
  out    dx,al
; adjust the address for a 16 bit channel

  mov    eax,[dword DAC_PHYSICAL_PAGE]
  push   eax
  shr    eax,16
; setup the page register
  mov    dx,[MVOurDMAPageReg]
  out    dx,al
  mov    bl,al
; reset the flip-flop, then output the address, then count
  mov    dl,[esi+dmaaddr._dmaclear]                      ;dh is still clear...
  out    dx,al                                          ;flush...
  pop    eax
  cmp    esi,offset DMA1AddrTable                        ;1st DMA controller?
  jz     @@F1                                           ;yes, continue on...
  shr    bl,1                                           ;no, divide the buffer in half
  rcr    ax,1                                           ;by shifting 17 bits
@@F1:
  mov    dx,[MVOurDMAddress]
  out    dx,al
  call   PCM_pause
  xchg   ah,al
  out    dx,al
  mov    eax,[DMA_Length] ;<------>
  lea    eax,[eax*2]
  cmp    esi,offset DMA1AddrTable                        ;is this the 2nd dma controller?
  jz     @@lodma03                                      ;no, use the full length
  shr    ax,1
  inc    dx                                             ;move to next port address
@@lodma03:
  dec    ax
  inc    dx                                             ;move to next port address
  out    dx,al
  call   PCM_pause
  xchg   ah,al
  out    dx,al
; before we enable the DMA, let's make sure the DRQ is controlled, not floating
  mov    al,[edi+mvstate._crosschannel]               ;get the state
  mov    edx,CROSSCHANNEL
  xor    edx,[_MVTranslateCode]
  or     al,bCCdrq                                      ;set the DRQ bit to control it
  out    dx,al
  mov    [edi+mvstate._crosschannel],al               ;and save the new state
; re-enable the dma now that all programming is done
  mov    al,[esi+dmaaddr._dmach]                         ;get the adjusted dma channel #
  sub    dx,dx
  mov    dl,[esi+dmaaddr._dmawrsmr]
  out    dx,al                                          ;& let'er loose (not moving though...)
; all done, return home...
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; SetupPCMDMAIO  --  Setup to output to the DAC
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc SetupPCMDMAIO
  push   edi
  mov    edi,[mvhwShadowPointer]
; setup the sample rate timer

  mov    al,00110110b                                   ;36h Timer 0 & square wave
  mov    edx,TMRCTLR
  xor    edx,[_MVTranslateCode]
  pushf
  cli
  out    dx,al                                          ;setup the mode, etc
  mov    [edi+mvstate._tmrctlr],al
  mov    ax,[edi+mvstate._samplerate]                 ;pre-calculated & saved in prior code
  mov    edx,SAMPLERATE
  xor    edx,[_MVTranslateCode]
  out    dx,al                                          ;output the timer value
  call   PCM_pause
  xchg   ah,al
  out    dx,al
  popf

; Setup the Sample Buffer Counter Timer (T1 & rate generator)
  mov    eax,[dword DMA_Length]                         ;get the count
  add    eax,1                                          ;make it 1 based (1 - 64k)
  mov    bl,[init_settings.DMA_no]                      ;is this a 16 bit channel?
  mov    bh,[MVSampleSize]                              ;CX = sample size, channel
  sub    cx,cx                                          ;ch = multiplier, cl=divider
  cmp    bx,0003h                                       ;8 bits on 8 bit channel?
  jbe    @@mdF1                                         ;yes, continue on...
  inc    cx                                             ;divide by 2
  cmp    bx,0007h                                       ;8 bits on 16 bit channel?
  jbe    @@mdF1                                         ;yes, continue on...
  xchg   ch,cl                                          ;multiply by 2
  cmp    bx,0203h                                       ;16 bits on 8 bit channel?
  jbe    @@mdF1                                         ;yes, continue on...
  sub    cx,cx                                          ;no multiply or divide
@@mdF1:
  dec    ax ;<------>
  shr    ax,cl                                          ;if 8 on 16 divide by 2
  xchg   ch,cl
  shl    ax,cl                                          ;if 16 on 8 multiply by 2
  sub    cx,cx                                          ;The buffer size is # of bytes, so
  neg    bh                                             ;we must convert it to the data size
  adc    cx,cx
  shr    ax,cl

  push   edi
  mov    edi,[mvhwShadowPointer]
  push   edx eax
  mov    al,01110100b                                   ;74h Timer 1 & rate generator
  mov    edx,TMRCTLR
  xor    edx,[_MVTranslateCode]
  cli
  out    dx,al
  mov    [edi+mvstate._tmrctlr],al                    ;local timer control register
  pop    eax
  mov    edx,SAMPLECNT
  xor    edx,[_MVTranslateCode]
  mov    [edi+mvstate._samplecnt],ax
  out    dx,al
  call   PCM_pause
  xchg   ah,al
  out    dx,al
  sti
  xchg   ah,al
  pop    edx edi

; Setup the system interrupt mask (IRQ mask)
  mov    dx,IRQ1MASKREG
  cmp    [init_settings.IRQ_no],8                       ;2nd IRQ controller?
  jl     @@irqcF2
  mov    dl,IRQ2MASKREG
@@irqcF2:
  in     al,dx                                          ;get the mask
  mov    ah,[mvTheIRQMask]
  not    ah
  and    al,ah                                          ;unmask the correct IRQ
  out    dx,al                                          ;then let the system know
; Setup the Interrupt Control Register

  mov    edx,INTRCTLRST                                  ;flush any pending interrupts
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  out    dx,al                                          ;of the PCM circuitry
  mov    edx,INTRCTLR
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  in     al,dx                                          ;get the real mask
  or     al,bICsampbuff                                 ;interrupt on sample buffer count
  out    dx,al                                          ;send it..
  mov    [edi+mvstate._intrctlr],al                   ;save it..
; enable the 12/16 bit stuff
  test   [_MVHWVersionBits],bMV101                      ;101 chip?
  jz     @@sdhpas1_05                                       ;no, don't touch this...
  mov    cx,((NOT(bSC216bit+bSC212bit)) shl 8) + bSC216bit+bSC212bit
  cmp    [mvSampleSize],1                               ;12 bit?
  jz     @@F3scsi
  mov    cx,((NOT(bSC216bit+bSC212bit)) shl 8) + bSC216bit
  cmp    [mvSampleSize],2                               ;16 bit?
  jz     @@F3scsi
  mov    cx,((NOT(bSC216bit+bSC212bit)) shl 8) + 0
@@f3scsi:
  mov    edx,SYSCONFIG2
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  in     al,dx
  and    al,ch                                          ;clear the bits
  or     al,cl                                          ;set the appropriate bits
  out    dx,al
@@sdhpas1_05:
; setup the direction, stereo/mono and DMA enable bits
  mov    al,bCCmono                                     ;get the stereo/mono mask bit
  and    al,0                                           ;al = bCCmono if in mono mode
  or     al,bCCdac                                      ;bit d6 of interrupt control register
  or     al,bCCenapcm
  mov    edx,CROSSCHANNEL
  xor    edx,[_MVTranslateCode]                          ;xlate the board address
  mov    ah,0fh + bCCdrq                                ;get a mask to load non PCM bits
  and    ah,[edi+mvstate._crosschannel]               ;grab all but PCM/DRQ/MONO/DIRECTION
  or     al,ah                                          ;merge the two states
  xor    al,bCCenapcm                                   ;disable the PCM bit
  out    dx,al                                          ;send to the hardware
  xor    al,bCCenapcm                                   ;enable the PCM bit
  out    dx,al                                          ;send to the hardware
  mov    [edi+mvstate._crosschannel],al               ; and save the new state
; Setup the audio filter sample bits
  mov    al,[edi+mvstate._audiofilt]
  or     al,bFIsrate+bFIsbuff                           ;enable the sample count/buff counters
  mov    edx,AUDIOFILT
  xor    edx,[_MVTranslateCode]                         ;xlate the board address
  out    dx,al
  mov    [edi+mvstate._audiofilt],al
  sti                                                   ; fly baby fly !
  pop    edi
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
label PAS16_sample_settings tDAC_ssetings
  tDAC_ssetings <11025,00222,0444,22050>
  tDAC_ssetings <16538,00332,0664,33075>
  tDAC_ssetings <22050,00442,0884,44100>
  tDAC_ssetings <33075,00662,1324,66150>
  tDAC_ssetings <44100,00882,1764,88200>

proc StartPlaying
  mov    [MVTypeOfSetup],DMAMASK
  mov    [byte DAC_shift_value],6
  movzx  eax,[byte init_settings.DAC_Samplerate]
  mov    ecx,size tDAC_ssetings
  mul    ecx
  mov    ebx,[dword eax+PAS16_sample_settings.DAC_sr]
  mov    [dword DAC_sampling_rate],ebx
  mov    ebx,[dword eax+PAS16_sample_settings.DAC_mix_buffer_size]
  mov    [dword DAC_mix_buffer_size],ebx
  lea    ebx,[ebx*4]
  mov    [DAC_double_buf2],ebx
  push   eax
  mov    bl,[init_settings.IRQ_no]
  lea    edx,[PAS16_virtualmixxer]
  mov    cx,cs
  call   setirqvector
  pop    eax
  mov    [byte MVSampleSize],2
  mov    ebx,[dword eax+PAS16_sample_settings.DMA_length]
  lea    ebx,[ebx*2]
  mov    [dword DMA_length],ebx
  mov    eax,[dword eax+PAS16_sample_settings.DMA_real_sr]
  mov    edi,[mvhwShadowPointer]
  mov    ecx,eax
  mov    eax,1193180
  xor    edx,edx
  div    ecx
  mov    [edi+MVState._samplerate],ax
;=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
; Program the DMA, then our board circuitry to start the interrupts
  call   LoadDMA                                        ;setup the DMA controller
  call   SetupPCMDMAIO                                  ;Setup the MV Hardware
  mov    [mvStatusWord],1                               ;set the global status word
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc find_pats_seqs
  mov    [byte funk_info.no_of_sequences],0
  mov    edi,[funk_hr_ptr]                          ; find no of patterns
  xor    al,al
  mov    ecx,256
@@find_patseq:
  cmp    [byte edi+tfunk_hr.order_list],0FFh
  je     @@end_patseq
  inc    [byte funk_info.no_of_sequences]
  cmp    [byte edi+tfunk_hr.order_list],al
  jbe    @@test_next
  mov    al,[byte edi+tfunk_hr.order_list]
@@test_next:
  inc    edi
  dec    ecx
  jnz    @@find_patseq
@@end_patseq:
  dec    [byte funk_info.no_of_sequences]
  inc    al
  mov    [byte funk_info.no_of_patterns],al
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc resync_sample_ptrs
  mov    al,[byte init_settings.card_type]
  cmp    al,SB_CARD
  je     @@DAC_CARD
  cmp    al,SBPRO_CARD
  je     @@DAC_CARD
  cmp    al,GUS_VARB_CARD
  je     @@DSP_CARD
  cmp    al,SB15EM_CARD
  je     @@DAC_CARD
  cmp    al,SB16_CARD
  je     @@DAC_CARD
  cmp    al,GUS_FIXB_CARD
  je     @@DSP_CARD
  cmp    al,PAS16_CARD
  je     @@DAC_CARD
  ret
@@DAC_CARD:
  mov    eax,[dword funk_sd_ptr]
  mov    [dword funk_info.sample_ptrs],eax
  jmp    @@calc
@@DSP_CARD:
  mov    [dword funk_info.sample_ptrs],32
@@calc:
  mov    ebx,[funk_hr_ptr]
  add    ebx,offset (tfunk_hr).funk_sb
  xor    edi,edi
  mov    cl,63
@@sync_sp:
  mov    eax,[dword ebx+tfunk_sb.length]
  add    eax,[dword edi+funk_info.sample_ptrs]
  add    ebx,size tfunk_sb
  add    edi,4
  mov    [dword edi+funk_info.sample_ptrs],eax
  dec    cl
  jnz    @@sync_sp
  mov    eax,[dword ebx+tfunk_sb.length]
  add    eax,[dword edi+funk_info.sample_ptrs]
  mov    [dword funk_info.sample_block_size],eax
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc upload_exteral_data
  cmp    [byte init_settings.card_type],GUS_VARB_CARD
  je     @@GUS_mem_move
  cmp    [byte init_settings.card_type],GUS_FIXB_CARD
  je     @@GUS_mem_move
  ret
@@GUS_mem_move:
  call   GUS_reset
  mov    esi,[dword funk_sd_ptr]
  mov    edi,[dword funk_info.sample_ptrs]
  mov    ecx,[dword funk_info.sample_block_size]
  sub    ecx,[dword funk_info.sample_ptrs]
  call   GUS_upload
  call   GUS_reset
  call   GUS_intready
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; eax = header buffer ptr                                                  ;
; ebx = sample buffer ptr                                                  ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc init_for_play
  mov    [byte funk_info.trek_status],PLAY
  mov    [byte funk_info.sequence_ofs_old],0ffh
  mov    [byte funk_info.pattern_ofs2_old],0ffh
  mov    [byte funk_info.sequence_ofs],0
  mov    [byte funk_info.pattern_ofs],0
  mov    [byte funk_info.tempo],04h
  mov    [byte funk_info.tempo_count],0
  mov    [byte funk_info.master_volume],0fh
  call   find_pats_seqs
  lea    edi,[funk_chan1]
  mov    cl,8
@@clear_funk_chan:
;Äcontrol systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  mov    [byte edi+tfunk_chan.command],0fh
  mov    [byte edi+tfunk_chan.com_val],0
  mov    [byte edi+tfunk_chan.comspd_count],0
  mov    [byte edi+tfunk_chan.sample],3fh
  mov    [dword edi+tfunk_chan.start],0ffffffffh
  mov    [dword edi+tfunk_chan.length],100000h
  mov    [byte edi+tfunk_chan.funkctrl],0
  mov    [byte edi+tfunk_chan.port_type],0
  mov    [byte edi+tfunk_chan.sample_ofs_parm],08h
  mov    [byte edi+tfunk_chan.vib_waveform],0
  mov    [byte edi+tfunk_chan.vol_vib_waveform],0
  mov    [byte edi+tfunk_chan.balance],80h
  mov    [byte edi+tfunk_chan.retrig_limit],4
  mov    [byte edi+tfunk_chan.arp_speed],3
;Änote systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  mov    [byte edi+tfunk_chan.note_command],0fh
  mov    [byte edi+tfunk_chan.note_com_val],0
  mov    [byte edi+tfunk_chan.note_comspd_count],0
  mov    [byte edi+tfunk_chan.note],03fh
  mov    [word edi+tfunk_chan.ifreq],0
  mov    [word edi+tfunk_chan.ifreq_vibrato],0
  mov    [word edi+tfunk_chan.ifreq_portdest],0
  mov    [dword edi+tfunk_chan.rfreq],0
  mov    [dword edi+tfunk_chan.rfreq_adjust],0
  mov    [dword edi+tfunk_chan.rfreq_portdest],0
  mov    [byte edi+tfunk_chan.vib_ptr],0
  mov    [byte edi+tfunk_chan.note_beat_count],0
;Ävolume systemÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
  mov    [byte edi+tfunk_chan.volume_command],0fh
  mov    [byte edi+tfunk_chan.volume_com_val],0
  mov    [byte edi+tfunk_chan.volume_comspd_count],0
  mov    [byte edi+tfunk_chan.volume],0
  mov    [byte edi+tfunk_chan.volume_vibrato],0
  mov    [byte edi+tfunk_chan.volume_portdest],0
  mov    [byte edi+tfunk_chan.rvolume],0
  mov    [byte edi+tfunk_chan.vol_vib_ptr],0
  mov    [byte edi+tfunk_chan.volume_beat_count],0

  mov    [dword edi+tfunk_chan.CARD_sample_ptr],0
  mov    [dword edi+tfunk_chan.CARD_freq],0
  mov    [dword edi+tfunk_chan.CARD_freq_attract],0
  mov    [dword edi+tfunk_chan.CARD_volume],0
  add    edi,size tfunk_chan
  dec    cl
  jnz    @@clear_funk_chan
  call   resync_sample_ptrs
  mov    [byte funk_info.trek_status],STOP
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; AUTODETECT CODE                                                          ;
;                                                                          ;
; process:                                                                 ;
;                                                                          ;
; a) it does PORT detection tests for the following cards (in order):      ;
;      - PAS16                                                             ;
;      - GUS CARD                                                          ;
;      - Creative Labs cards                                               ;
;                                                                          ;
;    if a particular card is found, then the routines then determine IRQ,  ;
;    and DMA settings of the card.                                         ;
;                                                                          ;
; PAS16: it first tests for a driver called "MVSOUND.SYS", which always    ;
; resides with any PAS card. If it can't find the driver, then it          ;
; assumes that the card isn't there. All information is obtained through   ;
; this driver.                                                             ;
; To my knowledge, most software will not work without this driver.        ;
;                                                                          ;
; GUS: AS far as i know, the ULTRASND= environment setting has been set    ;
; for every single GUS card ever installed. It is assumed that every       ;
; computer that has a GUS card will have ULTRASND= env. setting as well.   ;
; DMA and IRQ information is extracted from this env setting. If it can't  ;
; find it, then the detection routine will fail.                           ;
;                                                                          ;
; CT: After is has done a PORT test, it then determines the DSP version    ;
; of the card. This will determine if it's a SB, SBPRO, SB16 etc. Once     ;
; this is determined, it will go about searching for IRQ and DMA in the    ;
; following way:                                                           ;
;                                                                          ;
; SB1.SB20,SBPRO,SBPRO2: The BLASTER= env. setting is searched for         ;
; IRQ and DMA settings. If it can't find this enc setting, then the        ;
; detection routine will fail.                                             ;
;                                                                          ;
; SB16 and AWE32: Because these cards have software configurable IRQ and   ;
; DMA settings, the port status registers are read to determine these      ;
; settings. The routines will also detect the POST address of the          ;
; MPU-401 hardware.                                                        ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ad_line1               db 13,10,13,10,"Detecting card: $"
ad_line2               db "Couldn't find IRQ?"
ad_enter               db 13,10,"$"
ad_line3               db "Couldn't find DMA?",13,10,"$"
ad_line4               db "Found IRQ: $"
ad_line5               db "Found DMA: $"
ad_line6               db "MPU-401 port found at: $"
ad_line7               db "300h",13,10,"$"
ad_line8               db "Searching BLASTER environment...",13,10,"$"
ad_line9               db "Corrupted environment settings...can't find settings.",13,10,"$"
ad_line10              db "Found MIDI IRQ: $"

str_card0              db "Creative Labs compatable card detected at port: 2"
str_card0_dig          db "10h",13,10,"$"
str_card1              db "SoundBlaster 1.X$"
str_card2              db "SoundBlaster 2.0$"
str_card3              db "SoundBlaster PRO$"
str_card4              db "SoundBlaster 16$"
str_card5              db "Gravis Ultrasnd card detected at port: 2"
str_card5_dig          db "10h",13,10,"$"
str_card19             db "not"
str_card20             db " detected.",13,10,"$"

str_sel1               db "Multipal cards detected. Please select one of the following.",13,10,13,10,"$"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; eax= number
proc print_dec
  mov    esi,4*2
  mov    ecx,eax
@@dec83:
  mov    eax,ecx
  xor    edx,edx
  div    [dword esi+@@Dec_divider]
  mov    ecx,edx
  add    al,'0'

  mov    ah,0EH
  mov    bl,7
  int    10H

  sub    esi,4
  jge    @@dec83
  ret
@@dec_divider:
  dd 1,10
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; module modified by Scott Mackenzie, V1.08b                               ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc display_irq_dma
  mov    edx,offset ad_line4
  mov    ah,9
  int    21h
  movzx  eax,[byte init_settings.IRQ_no]
  call   print_dec
  mov    edx,offset ad_enter
  mov    ah,9
  int    21h
  mov    edx,offset ad_line5
  mov    ah,9
  int    21h
  movzx  eax,[byte init_settings.DMA_no]
  call   print_dec
  mov    edx,offset ad_enter
  mov    ah,9
  int    21h
  ret
endp

proc CT_environment
  mov    edx,offset ad_line8
  mov    ah,9
  int    21h
  push   es
  mov    ax,0EE02h              ; get DOS32 address info
  int    31h                    ; returns Environment address in EDI
  mov    ecx,10000h
@@SrchEnvrLoop:
  lea    esi,[@@blaster_string]
  mov    ecx,8
  repe   cmpsb
  jne    @@No_SBstring
  dec    edi
  mov    ebx,edi
  xor    ecx,ecx
@@s_irq:
  cmp    [byte ebx],"I"
  je     @@s_irq_exit
  inc    ebx
  dec    cx
  jnz    @@s_irq
  jmp    @@No_BLASTER
@@s_irq_exit:
  mov    al,[byte ebx+1]
  sub    al,"0"
  jnz    @@set_irq
  mov    al,10
  xor    ecx,ecx
@@set_irq:
  mov    [byte init_settings.IRQ_no],al
  mov    ebx,edi
  dec    cx
@@s_dma:
  cmp    [byte ebx],"D"
  je     @@s_dma_exit
  inc    ebx
  dec    cx
  jnz    @@s_dma
@@s_dma_exit:
  mov    al,[byte ebx+1]
  sub    al,"0"
  mov    [byte init_settings.DMA_no],al
  call   display_irq_dma
  pop    es
  ret
@@No_SBstring:
  xor    al,al
  mov    ecx,400h
  repne  scasb
  and    ecx,ecx
  jz     @@No_BLASTER
  cmp    [byte es:edi],0
  jnz    @@SrchEnvrLoop
@@No_BLASTER:
  mov    edx,offset ad_line9
  mov    ah,9
  int    21h
  pop    es
  mov    [byte init_settings.card_type],0ffh
  ret
@@blaster_string:
  db     "BLASTER="
endp

proc CT_detect
  mov    [dword init_settings.PORT_no],210h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],220h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],230h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],240h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],250h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],260h
  call   SB_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],280h
  call   SB_detect_port
  jnc    @@detected
  stc
  ret
@@detected:
  mov    eax,[dword init_settings.PORT_no]
  sub    eax,210h
  shr    eax,4
  add    [byte str_card0_dig],al
  mov    edx,offset str_card0
  mov    ah,9
  int    21h
  mov    al,0e1h
  call   SB_write_dac
  call   SB_read_dac
  mov    ah,al
  call   SB_read_dac
  cmp    ah,1
  je     @@sb15
  cmp    ah,2
  je     @@sb20
  cmp    ah,3
  je     @@sbpro
  cmp    ah,4
  jae    @@sb16
  ret
@@sb15:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  mov    [byte init_settings.card_type],SB15EM_CARD
  mov    edx,offset str_card1
  mov    ah,9
  int    21h
  mov    edx,offset str_card20
  mov    ah,9
  int    21h
  call   CT_environment
  jmp    @@done
  ret
@@sb20:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  cmp    al,0
  ja     @@sb202
  ret
@@sb202:
  mov    [byte init_settings.card_type],SB_CARD
  mov    edx,offset str_card2
  mov    ah,9
  int    21h
  mov    edx,offset str_card20
  mov    ah,9
  int    21h
  call   CT_environment
  jmp    @@done
  ret
@@sbpro:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  mov    [byte init_settings.card_type],SBPRO_CARD
  mov    edx,offset str_card3
  mov    ah,9
  int    21h
  mov    edx,offset str_card20
  mov    ah,9
  int    21h
  call   CT_environment
  jmp    @@done
  ret
@@sb16:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  mov    [byte init_settings.card_type],SB16_CARD
  mov    edx,offset str_card4
  mov    ah,9
  int    21h
  mov    edx,offset str_card20
  mov    ah,9
  int    21h

  mov    al,80h                                     ; get IRQ
  call   SB_read_mixer
  test   al,1
  jne    @@sb16_irq2
  test   al,2
  jne    @@sb16_irq5
  test   al,4
  jne    @@sb16_irq7
  test   al,8
  jne    @@sb16_irq10
  mov    [byte init_settings.card_type],0ffh
  mov    edx,offset ad_line2
  mov    ah,9
  int    21h
  jmp    @@done
@@sb16_irq2:
  mov    [byte init_settings.IRQ_no],2
  jmp    @@sb16_get_dma
@@sb16_irq5:
  mov    [byte init_settings.IRQ_no],5
  jmp    @@sb16_get_dma
@@sb16_irq7:
  mov    [byte init_settings.IRQ_no],7
  jmp    @@sb16_get_dma
@@sb16_irq10:
  mov    [byte init_settings.IRQ_no],10
@@sb16_get_dma:
  mov    al,81h                                     ; get DMA
  call   SB_read_mixer
  test   al,128
  jne    @@sb16_dma7
  test   al,64
  jne    @@sb16_dma6
  test   al,32
  jne    @@sb16_dma5
; ----------------------------------------------- added code Scott Mackenzie
  test   al,8
  jne    @@sb16_dma3
  test   al,2
  jne    @@sb16_dma1
  test   al,1
  jne    @@sb16_dma0
; --------------------
  mov    [byte init_settings.card_type],0ffh
  mov    edx,offset ad_line3
  mov    ah,9
  int    21h
  jmp    @@done
; ---------------------------------------------- added code Scott Mackenzie
@@sb16_dma0:
  mov    [byte init_settings.DMA_no],0
  jmp    @@display_sb16
@@sb16_dma1:
  mov    [byte init_settings.DMA_no],1
  jmp    @@display_sb16
@@sb16_dma3:
  mov    [byte init_settings.DMA_no],3
  jmp    @@display_sb16
; --------------------
@@sb16_dma5:
  mov    [byte init_settings.DMA_no],5
  jmp    @@display_sb16
@@sb16_dma6:
  mov    [byte init_settings.DMA_no],6
  jmp    @@display_sb16
@@sb16_dma7:
  mov    [byte init_settings.DMA_no],7
@@display_sb16:
  call   display_irq_dma
  mov    edx,offset ad_line6
  mov    ah,9
  int    21h
  mov    [MPU_addr],300h
  call   MPU401_Reset
  or     al,al
  jz     @@mpu_found
  mov    [MPU_addr],330h
  call   MPU401_Reset
  or     al,al
  jnz    @@mpu_notfound
@@mpu_found:
  mov    eax,[MPU_addr]
  sub    eax,300h
  shr    eax,4
  add    [byte ad_line7+1],al
  mov    edx,offset ad_line7
  mov    ah,9
  int    21h
  jmp    @@done
@@mpu_notfound:
  mov    edx,offset str_card19
  mov    ah,9
  int    21h
@@done:
  ret
endp
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; cy = not detected                                                        ;
; cn = detected                                                            ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc GUS_environment
  push   es
  mov    ax,0EE02h              ; get DOS32 address info
  int    31h                    ; returns Environment address in EDI
  cld
  mov    ecx,10000h
@@SrchEnvrLoop:
  lea    esi,[@@ultrasnd_string]
  mov    ecx,9
  repe   cmpsb
  jne    @@No_GUSstring
  mov    [byte init_settings.card_type],GUS_FIXB_CARD
  mov    ebx,[es:edi]                                  ;OK, Found the string 'ULTRASND='
  mov    eax,ebx                                       ;check for the valid paramters
  and    ebx,0ffff00ffh
  cmp    ebx,02C300032h                                ;'2x0,'
  jne    @@No_ULTRASND
  shr    eax,8
  call   @@get_digit                                   ;eeax = char digit in al
  mov    edx,eax                                       ;load port address
  shl    edx,4
  add    edx,200h
  sub    al,1h                                         ;port must be between  210h .. 260h
  cmp    al,5h
  ja     @@No_ULTRASND
  mov    [dword init_settings.PORT_no],edx
  add    edi,4
  xor    ecx,ecx
  movzx  eax,[byte es:edi]
  call   @@get_digit
  cmp    [byte eax+GUS_dma_table],0
  je     @@No_ULTRASND
  mov    [byte init_settings.DMA_no],al                ;get playback DMA
  inc    edi
  cmp    [byte es:edi],','
  jne    @@No_ULTRASND
  inc    edi
  movzx  eax,[byte es:edi]
  call   @@get_digit
  cmp    [byte eax+GUS_dma_table],0
  je     @@No_ULTRASND
  mov    [byte init_settings.DMA_no2],al                ;DMA record channel
  inc    edi
  cmp    [byte es:edi],','
  jne    @@No_ULTRASND
  xor    ebx,ebx
  inc    edi
  cmp    [byte es:edi],'1'
  jne    @@J61
  mov    bl,10
  inc    edi
@@J61:
  mov    eax,[es:edi]
  call   @@get_digit
  add    al,bl
  cmp    al,15
  ja     @@No_ULTRASND
  cmp    [byte eax+GUS_irq_table],0
  je     @@No_ULTRASND
  mov    BL,AL
  mov    [byte init_settings.IRQ_no],al
  inc    edi
  cmp    [byte es:edi],','
  jne    @@No_ULTRASND
  inc    edi
  cmp    [byte es:edi],'1'
  jne    @@J62
  mov    BH,10
  inc    edi
@@J62:
  mov    al,[es:edi]
  call   @@get_digit
  add    al,bh
  cmp    al,15
  ja     @@No_ULTRASND
  cmp    [byte eax+GUS_irq_table],0
  je     @@No_ULTRASND
  mov    [byte init_settings.IRQ_no2],al              ;MIDI irq number
  inc    edi
  cmp    [byte es:edi],','
  je     @@got_it
  cmp    [byte es:edi],' '
  je     @@got_it
  cmp    [byte es:edi],0
  jne    @@No_ULTRASND
@@got_it:
  pop    es

  mov    edx,offset ad_line4
  mov    ah,9
  int    21h
  movzx  eax,[byte init_settings.IRQ_no]
  call   print_dec
  mov    edx,offset ad_enter
  mov    ah,9
  int    21h


  mov    edx,offset ad_line10
  mov    ah,9
  int    21h
  movzx  eax,[byte init_settings.IRQ_no2]
  call   print_dec
  mov    edx,offset ad_enter
  mov    ah,9
  int    21h
  ret
@@No_GUSstring:
  mov    al,0                                          ; scan environment for next ZERO
  mov    ecx,400h
  repne  scasb
  or     ecx,ecx
  jz     @@No_ULTRASND
  cmp    [byte es:edi],0                               ; if double zeros then finished
  jnz    @@SrchEnvrLoop
@@No_ULTRASND:
  pop    es
  mov    edx,offset ad_line9
  mov    ah,9
  int    21h
  mov    [byte init_settings.card_type],0ffh
  ret
@@get_digit:
  sub    al,'0'
  jc @@No_digi
  cmp    al,9
  ja @@No_digi
  movzx  eax,al
  ret    0
@@No_digi:
  add    esp,4                                         ; ignore pushed EIP
  stc
  pop    es
  ret
@@ULTRASND_string:
  db     'ULTRASND='
endp

proc GUS_detect
  mov    [dword init_settings.PORT_no],210h
  call   GUS_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],220h
  call   GUS_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],230h
  call   GUS_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],240h
  call   GUS_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],250h
  call   GUS_detect_port
  jnc    @@detected
  mov    [dword init_settings.PORT_no],260h
  call   GUS_detect_port
  jnc    @@detected
  ret
proc GUS_detect_port
  mov    [dword gus_base],0
  mov    [dword gus_status],6
  mov    [dword gus_timercontrol],8
  mov    [dword gus_timerdata],9
  mov    [dword gus_irqdmacontrol],0bh
  mov    [dword gus_midictrl],100h
  mov    [dword gus_mididata],101h
  mov    [dword gus_voice],102h
  mov    [dword gus_command],103h
  mov    [dword gus_datalo],104h
  mov    [dword gus_datahi],105h
  mov    [dword gus_GF1_status],106h
  mov    [dword gus_dram],107h
  mov    eax,[dword init_settings.PORT_no]
  add    [dword gus_base],eax
  add    [dword gus_Status],eax
  add    [dword gus_timercontrol],eax
  add    [dword gus_timerdata],eax
  add    [dword gus_irqdmacontrol],eax
  add    [dword gus_midictrl],eax
  add    [dword gus_mididata],eax
  add    [dword gus_voice],eax
  add    [dword gus_Command],eax
  add    [dword gus_DataLo],eax
  add    [dword gus_DataHi],eax
  add    [dword gus_GF1_status],eax
  add    [dword gus_dram],eax
  lea    eax,[GUSi_freq_convert]
  mov    [dword CARD_freq_convert],eax
  lea    eax,[GUSi_volume_convert]
  mov    [dword CARD_volume_convert],eax
  mov    edx,[dword gus_command]
  mov    al,4Ch
  out    dx,al
  add    edx,2
  xor    al,al
  out    dx,al
  call   gus_Delay
  call   gus_Delay
  mov    edx,[dword gus_command]
  mov    al,4Ch
  out    dx,al
  add    edx,2
  mov    al,1
  out    dx,al
  mov    al,0AAh
  xor    edi,edi
  call   gus_mem_out
  mov    edi,100h
  mov    al,55h
  call   gus_mem_out
  xor    esi,esi
  call   gus_mem_in
  push   eax
  mov    edx,[dword gus_command]
  mov    al,4Ch
  out    dx,al
  add    edx,2
  mov    al,1
  out    dx,al
  pop    eax
  cmp    al,0AAh
  jnz    @@Nope
  clc
  ret
@@Nope:
  stc
  ret
endp
@@detected:
  mov    eax,[dword init_settings.PORT_no]
  sub    eax,210h
  shr    eax,4
  add    [byte str_card5_dig],al
  mov    edx,offset str_card5
  mov    ah,9
  int    21h
  call   GUS_environment
  cmp    [byte init_settings.card_type],0ffh
  je     @@ignore
  call   GUS_find_mem
@@ignore:
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                          ;
; cy = not detected                                                        ;
; cn = detected                                                            ;
;                                                                          ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
proc PAS_detect
  xor    edi,edi
  call   mvGetHWVersion
  jnc    @@detected
  mov    [byte init_settings.card_type],0ffh
  ret
@@detected:
  mov    [byte init_settings.card_type],PAS16_CARD
  call   MVInitStatePtr
  ret
endp

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;                                                                         ;
; Interface routines                                                      ;
;                                                                         ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;bl = card override: 0=PAS,1=GUS,2=CT card
;al = IRQ no, ah = DMA no
proc FNK_autodetect
  push   eax
  cmp    bl,1
  je     @@gus_d
  cmp    bl,2
  je     @@ct_d

  call   PAS_detect
  cmp    [byte init_settings.card_type],0ffh
  jne    @@card_detected
@@gus_d:
  call   GUS_detect
  cmp    [byte init_settings.card_type],0ffh
  jne    @@card_detected
@@ct_d:
  call   CT_detect
  cmp    [byte init_settings.card_type],0ffh
  jne    @@card_detected
  cmp    [byte init_settings.card_type],0ffh
  pop    eax
  stc
  ret
@@card_detected:
  pop    eax
  cmp    al,0ffh
  je     @@ignIRQ
  mov    [byte init_settings.IRQ_no],al
@@ignIRQ:
  cmp    ah,0ffh
  je     @@ignDMA
  mov    [byte init_settings.DMA_no],ah
@@ignDMA:
  clc
  ret
endp

;eax = DMA buffer ptr
proc FNK_card_init
  add    [dword DAC_mix_buffer],eax
  add    [dword DAC_mix_buffer_right],eax
  add    [dword DAC_mix_buffer2],eax
  push   eax
  mov    ax,0EE02h
  int    31h
  neg    ebx
  pop    eax
  sub    eax,ebx
  add    [dword DAC_PHYSICAL_PAGE],eax
  mov    edi,[dword DAC_mix_buffer]                    ; clear buffers
  mov    ecx,1000h/2
  mov    eax,8000h
  rep    stosw
  mov    edi,[dword DAC_mix_buffer_right]              ; clear buffers
  mov    ecx,1000h/2
  mov    eax,8000h
  rep    stosw
  mov    ecx,2000h
  mov    edi,[dword DAC_mix_buffer2]
  mov    al,80h
  rep    stosb
  cmp    [byte init_settings.card_type],SB_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],SBPRO_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],GUS_VARB_CARD
  je     @@gus
  cmp    [byte init_settings.card_type],SB15EM_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],SB16_CARD
  je     @@sb16
  cmp    [byte init_settings.card_type],GUS_FIXB_CARD
  je     @@gus
  cmp    [byte init_settings.card_type],PAS16_CARD
  je     @@pas_family
  ret
@@sb08:
  call   SB08_init
  jc     @@sb08_e
  call   SB08_deinit
  call   SB08_init
@@sb08_e:
  ret
@@sb16:
  mov    ecx,2000h/2
  mov    edi,[dword DAC_mix_buffer2]
  xor    eax,eax
  rep    stosw
  call   SB16_init
  ret
@@gus:
  call   GUS_init
  ret
@@pas_family:
  mov    ecx,2000h/2
  mov    edi,[dword DAC_mix_buffer2]
  xor    eax,eax
  rep    stosw
  call   InitPCM
  call   StartPlaying
  ret
endp

proc FNK_card_deinit
  cmp    [byte init_settings.card_type],SB_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],SBPRO_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],GUS_VARB_CARD
  je     @@gus
  cmp    [byte init_settings.card_type],SB15EM_CARD
  je     @@sb08
  cmp    [byte init_settings.card_type],SB16_CARD
  je     @@sb16
  cmp    [byte init_settings.card_type],GUS_FIXB_CARD
  je     @@gus
  cmp    [byte init_settings.card_type],PAS16_CARD
  je     @@pas_family
  ret
@@sb08:
  call   SB08_deinit
  ret
@@sb16:
  call   SB16_deinit
  ret
@@gus:
  call   GUS_deinit
  ret
@@pas_family:
  call   StopPCM
  ret
endp

;eax = funksong pointer
proc FNK_setup_player
  mov    [funk_hr_ptr],eax
  call   find_pats_seqs
  movzx  ecx,[byte funk_info.no_of_patterns]
  mov    eax,600h
  mul    ecx
  mov    ebx,size tfunk_hr
  add    ebx,eax
  mov    eax,[funk_hr_ptr]
  add    ebx,eax
  mov    [funk_sd_ptr],ebx
  call   init_for_play
  call   upload_exteral_data
  ret
endp

proc FNK_PLAY
  mov    [byte funk_info.trek_status],PLAY
  ret
endp

proc FNK_STOP
  mov    [byte funk_info.trek_status],STOP
  ret
endp

proc FNK_teststatus
  cmp    [byte funk_info.trek_status],PLAY
  ret
endp

;al = master volume (0-f)
proc FNK_set_mvolume
  mov    [byte funk_info.master_volume],al
  ret
endp

;returns al = master volume (0-f)
proc FNK_get_mvolume
  mov    al,[byte funk_info.master_volume]
  ret
endp

;al: 0=11025hz,1=16538hz,2=22050hz,3=33075hz,4=44100hz
proc FNK_set_sr
  cmp    al,4
  ja     @@ign
  mov    [byte init_settings.DAC_Samplerate],al
@@ign:
  ret
endp
