.286

segment data
w equ word ptr
b equ byte ptr
num_voices equ 14

;**************************************************************************
;***                    D A T A   S E G M E N T                         ***
;**************************************************************************

 found db  ?

;**************************************************************************
;***                      A L L O C A T I O N S                         ***
;**************************************************************************

  Play_Voice equ 0
  Stop_Voice equ 3
  Bit8       equ 0
  Bit16      equ 4
  No_Loop    equ 0
  With_Loop   equ 8
  Unidirect  equ 0
  Bidirect   equ 16
  Go_forw    equ 0
  Go_Back    equ 64
data ends

;**************************************************************************
;***                      C O D E   S E G M E N T                       ***
;**************************************************************************

segment code
  assume cs:code, ds:data

;**************************************************************************
;***               P U B L I C  -  D E C L A R A T I O N S              ***
;**************************************************************************


  u_base     dw 240h
  u_status   dw u_base+006h
  u_voice    dw u_base+102h
  u_command  dw u_base+103h
  u_Datalo   dw u_base+104h
  u_Datahi   dw u_base+105h
  u_DramIO   dw u_base+107h



oldUVolumes dw 01000h,0B000h,0B100h,0B200h,0B300h,0B400h,0B500h,0B600h,0B700h
         dw 0B800h,0B900h,0BA00h,0BB00h,0BC00h,0BD00h,0BE00h,0BF00h
         dw 0C000h,0C100h,0C200h,0C300h,0C400h,0C500h,0C600h,0C700h
         dw 0C800h,0C900h,0CA00h,0CB00h,0CC00h,0CD00h,0CE00h,0CF00h
         dw 0D000h,0D100h,0D200h,0D300h,0D400h,0D500h,0D600h,0D700h
         dw 0D800h,0D900h,0DA00h,0DB00h,0DC00h,0DD00h,0DE00h,0DF00h
         dw 0E000h,0E100h,0E200h,0E300h,0E400h,0E500h,0E600h,0E700h
         dw 0E800h,0E900h,0EA00h,0EB00h,0EC00h,0ED00h,0EE00h,0EF00h


UVolumes  DW 1500h
	DW 40004,42600,44752,45648,46544,47624,48448,49232
	DW 50048,50584,51112,51656,52184,52584,52976,53376
	DW 53752,54016,54280,54520,54768,55024,55280,55544
	DW 55776,56048,56288,56536,56784,56992,57184,57384
	DW 57616,57752,57888,58000,58112,58248,58368,58480
	DW 58600,58720,58840,58960,59088,59208,59336,59464
	DW 59584,59720,59816,59944,60072,60176,60312,60408
	DW 60544,60648,60784,60888,60992,61064,61176,61248


Voice_Divisor db 43,40,37,35,33,31,30,28,27,26,25,24,23,22,21,20,20,19,18

FFtable dw   66,   70,   74,   78,   83,   88,   93,   99,  104,  111
        dw  117,  124,  132,  139,  148,  156,  166,  176,  186,  197
        dw  209,  221,  234,  248,  263,  279,  295,  313,  331,  351
        dw  372,  394,  418,  442,  469,  497,  526,  557,  591,  626
        dw  663,  702,  744,  788,  835,  885,  938,  993, 1052, 1115
        dw 1181, 1251, 1326, 1405, 1488, 1577, 1671, 1770, 1875, 1987
        dw 2105, 2230, 2362, 2503, 2652, 2809, 2977, 3154, 3341, 3540


Modoctave  dw 1712,1616,1525,1440,1359,1283,1211
           dw 1143,1078,961,907,856,808,763,720
           dw 679,641,605,571,539,509,480,453,428
           dw 404,381,360,340,321,303,286,270,254
           dw 240,227,214,202,191,180,170,160,151
           dw 143,135, 127,120,113,107,101,95,90
           dw 85,80,76,71,67,64,60,57,54,50,47,45
           dw 42,40,38,36,34,32,30

public U_StartVoice
public u_VoiceBalance
public u_VoiceVolume
public u_delay
public u_Initialize
public u_Voicefreq
public Ultra_Mem2Gus
public u_Voicedata
public ffactor
public notennr
public dos_getmem
public dos_freemem
public detect_gus
public init_gus_base
public GusSound_on
public GusSound_off
public voice_rampin
public voice_slidein

u_delay proc pascal
; **************************************************************************
; *** Waits the required time for double writes                          ***
; **************************************************************************
  mov dx,300h
  in al,dx
  in al,dx
  in al,dx
  in al,dx
  in al,dx
  in al,dx
  in al,dx
  ret
u_delay endp

U_StartVoice proc pascal Nr,Mode : byte
; **************************************************************************
; *** Starts output to a GUS channel                                     ***
; **************************************************************************
  mov dx,w u_voice                ; select voice
  mov al,byte ptr Nr
  out dx,al
  mov dx,w u_command
  mov al,0                        ; Voice mode
  out dx,al
  mov dx,w u_datahi
  mov al,Mode                     ; set MODE byte
  out dx,al
  ret
U_StartVoice endp

u_VoiceBalance proc pascal Nr,balance : byte
; **************************************************************************
; *** Sets the Pan position for a channel (0 - 15)                       ***
; **************************************************************************
  mov dx,w u_voice                ; select voice
  mov al,byte ptr Nr
  out dx,al
  mov dx,w u_command              ; set Pan position command
  mov al,0Ch
  out dx,al
  mov dx,w u_datahi               ; write position
  mov al,balance
  out dx,al
  ret
u_VoiceBalance endp

u_VoiceVolume proc pascal Nr:byte,Vol:word
; **************************************************************************
; *** Sets the volume for a channel  (0 - 63)                            ***
; **************************************************************************
    mov dx,w u_voice              ; select voice
    mov al,Nr
    out dx,al
    mov dx,w u_command            ; set volume command
    mov al,9
    out dx,al
    mov dx,w u_datalo             ; load GUS volume from table
    mov di,vol                    ; and set
    shl di,1
    mov ax,word ptr [offset uVolumes + di]
    out dx,ax
    ret
u_VoiceVolume  endp

u_Initialize proc near
; **************************************************************************
; *** Initializes ultrasound                                             ***
; **************************************************************************
  mov bx,w u_command
  mov cx,w u_datahi
  mov dx,bx
  mov al,4ch                      ; select Init - Register
  out dx,al
  mov dx,cx
  mov al,0                        ; run Init
  out dx,al
  call u_delay                    ; wait
  call u_delay
  mov dx,bx
  mov al,4ch
  out dx,al
  mov dx,cx
  mov al,1                        ; end Init
  out dx,al
  call u_delay
  call u_delay
  mov dx,bx                       ; reset DMA Control Register
  mov al,41h
  out dx,al
  mov dx,cx
  mov al,0
  out dx,al
  mov dx,bx                       ; reset Timer Control Register
  mov al,45h
  out dx,al
  mov dx,cx
  mov al,0
  out dx,al
  mov dx,bx                       ; reset Sampling Control Register
  mov al,49h
  out dx,al
  mov dx,cx
  mov al,0
  out dx,al
  mov dx,bx                       ; set number voices
  mov al,0Eh
  out dx,al
  add dx,2
  mov al,Num_Voices
  or  al,0C0h
  out dx,al
  mov dx,w u_status               ; empty DMA interrupts
  in al,dx
  mov dx,bx
  mov al,41h
  out dx,al
  mov dx,cx
  in al,dx
  mov dx,bx                       ; empty sampling interrupts
  mov al,49h
  out dx,al
  mov dx,cx
  in al,dx
  mov dx,bx                       ; read IRQ status register
  mov al,8Fh                      ; ==> Now there are unprocessed 
  out dx,al                       ;     interrupts
  mov dx,cx
  in al,dx
  push bx                         ; disable the voices in the loop
  push cx
  mov cx,0
@VoiceClearLoop:
  mov dx,w u_Voice                ; select voice
  mov al,cl
  out dx,al
  inc dx
  mov al,0                        ; set voice mode
  out dx,al
  add dx,2
  mov al,3                        ; stop voice
  out dx,al
  sub dx,2                        ; set volume to 0
  mov al,0dh
  out dx,al
  add dx,2
  mov al,3
  out dx,al
  inc cx
  cmp cx,32                       ; repeat for all voices
  jnz @VoiceClearLoop
  pop cx
  pop bx
  mov dx,bx                       ; any interrupts that may have occurred
  mov al,41h                      ; "process"
  out dx,al
  mov dx,cx
  in al,dx
  mov dx,bx
  mov al,49h
  out dx,al
  mov dx,cx
  in al,dx
  mov dx,bx
  mov al,8fh
  out dx,al
  mov dx,cx
  in al,dx
  mov dx,bx                       ; perform reset
  mov al,4ch
  out dx,al
  mov dx,cx                       ; enable GF1 master IRQ
  mov al,7
  out dx,al
  ret
u_Initialize endp

u_Voicefreq proc pascal Nr:byte,Freq:word
; **************************************************************************
; *** Sets the frequency at which the channel is played back             ***
; **************************************************************************
  mov dx,w u_voice                ; address voice
  mov al,Nr
  out dx,al
  mov dx,w u_command              ; write voice frequency command
  mov al,1
  out dx,al                       ; freq := freqency DIV
  xor bx,bx                       ;         voice_divisor[num_voices-13]
  mov bl,num_voices
  mov ax,freq
;  mov di,bx
;  sub di,14
;  xor bx,bx
;  xor dx,dx
;  mov bl,byte ptr [voice_divisor+di]
;  div bx
  mov dx,w u_datalo
  out dx,ax
  ret
u_Voicefreq endp


Ultra_Mem2Gus proc pascal sampp:dword,start:dword,length1:word
; **************************************************************************
; *** Copies a memory area from RAM to GUS RAM                           ***
; **************************************************************************
    push ds
    push si
    mov si,[bp+12]                ; segment
    mov ds,si
    mov si,[bp+10]                ; offset
    mov dx,w u_command            ; set hi byte of GUS DRAM address
    mov al,44h
    out dx,al
    mov dx,w u_datahi
    mov ax,[bp+08]                ; hstart
    out dx,al
    mov cx,[bp+4]                 ; load length
@copy_loop:
    mov dx,w u_command            ; set lo byte of GUS DRAM address
    mov al,43h
    out dx,al
    mov dx,w u_datalo
    mov ax,[bp+06]                ; lstart
    out dx,ax
    mov dx,w u_dramio             ; load byte and output
    lodsb
    out dx,al
    cmp word ptr [bp+06],0ffffh   ; lstart = 0ffffh ?
    je @overflow
    inc word ptr [bp+06]          ; lstart++
    jmp @continue
@overflow:
    inc word ptr [bp+08]          ; hstart ++
    mov word ptr [bp+06],0        ; lstart to 0
    mov dx,w u_command            ; set hi byte of GUS DRAM address
    mov al,44h
    out dx,al
    mov dx,w u_datahi
    mov ax,[bp+08]                ; hstart
    out dx,al
@continue:
    loop @copy_loop
    pop si
    pop ds
    ret
Ultra_Mem2Gus endp

u_Voicedata proc pascal start,lsta,llength1:dword,Nr:word
; **************************************************************************
; *** Sets parameters for a channel                                      ***
; **************************************************************************
    mov dx,w u_voice              ; select voice
    mov ax,nr
    out dx,al
    mov dx,w u_command            ; set voice beginning
    mov al,0ah
    out dx,al
    mov ax, word ptr [start+2]
    mov cx, word ptr [start]
    mov bx,cx
    shr ax,7
    shr cx,7
    shl bx,9
    or  ax,bx
    mov dx,w u_datalo
    out dx,ax
    mov dx,w u_command
    mov al,0bh
    out dx,al
    mov dx,w u_datalo
    mov ax,word ptr [start]
    shl ax,9
    out dx,ax
    mov dx,w u_command            ; set loop start
    mov al,2
    out dx,al
    mov ax, word ptr [lsta]
    mov cx, word ptr [lsta+2]
    mov bx,cx
    shr ax,7
    shr cx,7
    shl bx,9
    or  ax,bx
    mov dx,w u_datalo
    out dx,ax
    mov dx,w u_command
    mov al,3
    out dx,al
    mov dx,w u_datalo
    mov ax,word ptr [lsta]
    shl ax,9
    out dx,ax
    mov dx,w u_command            ; set loop end
    mov al,4
    out dx,al
    mov ax, word ptr [llength1]
    mov cx, word ptr [llength1+2]
    mov bx,cx
    shr ax,7
    shr cx,7
    shl bx,9
    or  ax,bx
    mov dx,w u_datalo
    out dx,ax
    mov dx,w u_command
    mov al,5
    out dx,al
    mov dx,w u_datalo
    mov ax,word ptr [llength1]
    shl ax,9
    out dx,ax
   ret
u_Voicedata endp

ffactor proc pascal t:word
; **************************************************************************
; ***  Returns the frequency of the sound passed in "t"                  ***
; **************************************************************************
 mov di,t
 sub di,5
 shl di,1
 mov ax,word ptr [offset fftable+di]
 ret
ffactor endp

Notennr proc pascal height:word
; **************************************************************************
; *** Defines the number of the note above the "height" of the sound     ***
; *** from the MOD file                                                  ***         ***
; **************************************************************************
  mov found,1
  xor di,di
@loop:
  mov ax,word ptr modoctave[di]
  cmp height,ax
  ja note_found
  add di,2
;  cmp di,128
  cmp di,140
  jae @continue_working
  jmp @loop
note_found:
  mov found,0
@continue_working:
  mov ax,255
  cmp found,0
  jne end_notennr
  mov ax,di
  shr ax,1
  inc ax
end_notennr:
  ret
notennr endp

dos_getmem proc pascal pointer:dword,amount:word
; **************************************************************************
; *** Allocates a (max. 64K) memory area in DOS RAM                      ***
; **************************************************************************
  push ds
  mov bx,amount
  shr bx,4
  inc bx
  mov ah,48h
  int 21h
  mov bx,w [pointer+2]
  mov ds,bx
  mov bx,w [pointer]
  mov w [bx],0
  mov w [bx+2],ax
  pop ds
  ret
dos_getmem endp

dos_freemem proc pascal pointer:dword
; **************************************************************************
; *** Frees up an area allocated by dos_getmem                           ***
; **************************************************************************
  mov ax,word ptr [pointer+2]
  mov es,ax
  mov ah,49h
  int 21h
  ret
dos_freemem endp

detect_gus proc near
; **************************************************************************
; *** Routine for detecting the Gravis Ultrasound. Detects the base port ***
; *** The function returns 0 if the card was found, otherwise            ***
; *** it returns 1.                                                      ***
; **************************************************************************
  mov di,1F0h
@detect_loop:                     ; test possible ports in a loop
  add di,10h
  mov dx,di
  add dx,103h                     ; attempt initialization
  mov al,4Ch
  out dx,al
  mov dx,di
  add dx,105h
  mov al,0
  out dx,al ;?????
  call u_delay
  call u_delay
  mov dx,di
  add dx,103h
  mov al,4Ch
  out dx,al
  mov dx,di
  add dx,105h
  mov al,1
  out dx,al ;????
  mov dx,di                       ; attempt to write data to
  add dx,103h                     ; GUS RAM
  mov al,43h
  out dx,al
  mov dx,di
  add dx,105h
  mov al,0h
  out dx,al
  mov dx,di
  add dx,103h
  mov al,44h
  out dx,al
  mov dx,di
  add dx,105h
  mov al,0h
  out dx,al
  mov dx,di
  add dx,107h
  mov al,0AAh
  out dx,al
  call u_delay                    ; wait so that the GF1 doesn't 
  call u_delay                    ; botch things up for us
  call u_delay
  call u_delay
  call u_delay
  call u_delay
  xor ax,ax                       ; read back value from GUS RAM
  mov dx,di
  add dx,107h
  in  al,dx
  cmp al,0AAh                     ; read value = written value ?
  je  @card_found                 ; hey, found the card !
  cmp di,280h
  jae @card_not_found             ; no card at ports to find  :(
  jmp @detect_loop                ; try new port
@card_found:
  mov w u_base,di                 ; initialize base register of card
  mov ax,di
  add ax,6
  mov w u_status,ax
  mov ax,di
  add ax,102h
  mov w u_voice,ax
  mov ax,di
  add ax,103h
  mov w u_command,ax
  mov ax,di
  add ax,104h
  mov w u_datalo,ax
  mov ax,di
  add ax,105h
  mov w u_datahi,ax
  mov ax,di
  add ax,107h
  mov w u_dramIO,ax
  mov ax,0
  jmp @end_carddetection
@card_not_found:
  mov ax,1
@end_carddetection:
  ret
detect_gus endp

init_gus_base proc pascal gbase : word;
; **************************************************************************
; *** Routine for detecting the Gravis Ultrasound. Detects the base port.***
; *** The function returns 0 if the card was found, otherwise            ***
; *** it returns 1.                                                      ***
; **************************************************************************
  mov di,gbase
  mov dx,di
  add dx,103h                     ; attempt initialization
  mov al,4Ch
  out dx,al
  mov dx,di
  add dx,105h
  mov al,0
  out dx,al ;?????
  call u_delay
  call u_delay
  mov dx,di
  add dx,103h
  mov al,4Ch
  out dx,al
  mov dx,di
  add dx,105h
  mov al,1
  out dx,al ;????

  mov w u_base,di                 ; initialize base register of card
  mov ax,di
  add ax,6
  mov w u_status,ax
  mov ax,di
  add ax,102h
  mov w u_voice,ax
  mov ax,di
  add ax,103h
  mov w u_command,ax
  mov ax,di
  add ax,104h
  mov w u_datalo,ax
  mov ax,di
  add ax,105h
  mov w u_datahi,ax
  mov ax,di
  add ax,107h
  mov w u_dramio,ax
  mov ax,0
  ret
init_gus_base endp


GusSound_off proc near
; **************************************************************************
; *** Switches off sound output of GUS                                   ***
; **************************************************************************
  mov dx,w u_base
  in  al,dx
  or  al,2
  out dx,al
  ret
GusSound_off endp

GusSound_on proc near
; **************************************************************************
; *** Switches on sound output (wave table) of GUS.                      ***
; **************************************************************************
  mov dx,w u_base
  in  al,dx
  and al,0FDh
  out dx,al
  ret
GusSound_on endp

voice_rampin proc pascal voice:byte,vol : word;
; **************************************************************************
; *** An alternative to directly setting the volume of a voice.          ***
; *** While the player loses some aggression, it considerably reduces    ***
; *** any crackling/static.                                              ***
; **************************************************************************
  mov dx,w u_voice                ; select voice
  mov al,byte ptr voice
  out dx,al
  mov dx,w u_command              ; set ramping factor
  mov al,6
  out dx,al
  mov dx,w u_datahi
  mov al,00111111b
  out dx,al
  mov dx,w u_command              ; adjust current volume
  mov al,9
  out dx,al
  mov dx,w u_datahi
  mov al,00010010b
  out dx,al
  mov dx,w u_command              ; set ramping start volume
  mov al,7
  out dx,al
  mov dx,w u_datahi
  mov al,00010010b
  out dx,al
  mov dx,w u_command              ; set ramping end volume
  mov al,8
  out dx,al
  mov dx,w u_datahi
  mov di,word ptr vol
  shl di,1
  mov ax,word ptr [offset uvolumes + di]
  shr ax,8
  out dx,al
  mov dx,w u_command              ; set ramping direction in volume 
  mov al,0dh                      ; control register
  out dx,al
  mov dx,w u_datahi
  mov al,0
  out dx,al
  ret
voice_rampin endp

voice_slidein proc pascal nr,speed : byte,vol : word;
  mov dx,w u_voice                ; select voice
  mov al,byte ptr nr
  out dx,al
  mov dx,w u_command              ; set ramping factor
  mov al,6
  out dx,al
  mov dx,w u_datahi
  mov al,byte ptr speed
  out dx,al
  mov dx,w u_command              ; adjust current volume
  mov al,9
  out dx,al
  mov dx,w u_datahi
  mov al,00010010b
  out dx,al
  mov dx,w u_command              ; set ramping start volume
  mov al,7
  out dx,al
  mov dx,w u_datahi
  mov al,00010010b
  out dx,al
  mov dx,w u_command              ; set ramping end volume
  mov al,8
  out dx,al
  mov dx,w u_datahi
  mov di,word ptr vol
  shl di,1
  mov ax,word ptr [offset uvolumes + di]
  shr ax,8
  out dx,al
  mov dx,w u_command              ; set ramping direction in volume
  mov al,0dh                      ; control register
  out dx,al
  mov dx,w u_datahi
  mov al,0
  out dx,al
  ret
voice_slidein endp

public gus_speaker_on
gus_speaker_on proc pascal
  mov dx,u_base
  mov al,4
  out dx,al
  ret
gus_speaker_on endp

public get_voice_position
get_voice_position proc pascal voice : word
  mov dx,w u_command
  mov al,4
  out dx,al
  mov dx,w u_datahi
  in  ax,dx
  mov cx,ax

  ret
get_voice_position endp

public get_detected_base
get_detected_base proc pascal
  mov ax,u_base
  ret
get_detected_base endp

code ends
end

