home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Hack-Phreak Scene Programs
/
cleanhpvac.zip
/
cleanhpvac
/
ASMCODE.ZIP
/
GUSUTIL.ASM
< prev
next >
Wrap
Assembly Source File
|
1994-11-02
|
87KB
|
3,565 lines
;══════════════════════════════════════════════════════════════════════
;
; Gravis Utilities for Borland\Turbo Pascal.
;
; Copyright (c) 1994 by Jonathan E. Wright and AmoebaSoft.
;
;══════════════════════════════════════════════════════════════════════
;
; NOTES:
;
;══════════════════════════════════════════════════════════════════════
;
; If the IRQ handler is enabled, you must be sure to set the voice's
; Start, End and Current Locations before setting the IRQAtEnd bit with
; a call to VoiceMode or GUS_VoiceMode. If you set the IRQ bit while
; the Current voice location is pointing to the end location, i.e. the
; voice has been played once, the voice can be turned off immediately by
; the IRQ handler and never get a chance to be heard.
;
; The IRQ handler doesn't appear to be necessary for normal sample
; playback, especially if the samples are high quality and have been
; modified to have ramps at the beginning and end to stop clicking (i.e.
; the sample fades in from 0 very quickly at the beginning and fades out
; to zero very quickly at the end).
;
; A voice with Looping set and IRQ's enabled will be cut off be the IRQ
; handler when the end of the sample is reached and will not loop. To
; loop a voice, start it without the IRQAtEnd bit set.
;
; This file contains 80386 specific instructions and won't work on lamer
; processors.
;
; The MOD player interprets Set Volume (0Fh) commands with a value > 64
; as Set Flag commands. If a Set Volume command with an argument of 65
; is given, then the variable MODFlag will be set to 65 - 65 = 0. If a
; Set Volume command with an argument of 66 is given, the flag will be
; set to 66 - 65 = 1. This allows a program to synchronize itself with
; events in the music, based on the value of MODFlag.
;
;══════════════════════════════════════════════════════════════════════
;follwing registers are GUS_Base + value
;
;GUS_Status EQU 06h
;GUS_TimerCon EQU 08h
;GUS_TimerData EQU 09h
;GUS_IRQDMACon EQU 0Bh
;GUS_MidiCon EQU 100h
;GUS_MidiData EQU 101h
;GUS_Voice EQU 102h
;GUS_Command EQU 103h
;GUS_DataLo EQU 104h
;GUS_DataHi EQU 105h
;GUS_DRAMIO EQU 107h
GUS_SetVoiceMode EQU 00h
GUS_SetVoiceFreq EQU 01h
GUS_RampVolIncr EQU 06h
GUS_RampVolStart EQU 07h
GUS_RampVolEnd EQU 08h
GUS_CurVolume EQU 09h
GUS_VolControl EQU 0Dh
GUS_Stop EQU 3
GUS_Bit16 EQU 4
GUS_Loop EQU 8
GUS_Bidirec EQU 16
GUS_IRQAtEnd EQU 32
GUS_Backward EQU 64
GUS_Scale0 EQU 0
GUS_Scale8 EQU 1
GUS_Scale64 EQU 2
GUS_Scale512 EQU 3
GUS_RampStop EQU 3
GUS_RampRoll EQU 4
GUS_RampLoop EQU 8
GUS_RampBidir EQU 16
GUS_RampIRQ EQU 32
GUS_RampDec EQU 64
NoteSize EQU 6
PatLineSize EQU 8 * NoteSize ; maxtracks * notetype size
InstrTypeSize EQU 44
ChannelInfoSize EQU 83
PatternOfs EQU 1364
ScriptOfs EQU 1876
NumPatsOfs EQU ScriptOfs + 128
EndJumpOfs EQU NumPatsOfs + 1
.386
DATA SEGMENT WORD PUBLIC USE16
EXTRN GUS_Base : WORD
EXTRN GUS_IRQ : WORD
EXTRN GUS_Status : WORD
EXTRN GUS_TimerCon : WORD
EXTRN GUS_TimerData : WORD
EXTRN GUS_IRQDMACon : WORD
EXTRN GUS_MidiCon : WORD
EXTRN GUS_MidiData : WORD
EXTRN GUS_Voice : WORD
EXTRN GUS_Command : WORD
EXTRN GUS_DataLo : WORD
EXTRN GUS_DataHi : WORD
EXTRN GUS_DRAMIO : WORD
EXTRN GUS_Mixer : BYTE
EXTRN ActiveVoices : BYTE
EXTRN CurVoice : BYTE
EXTRN OrigRate : WORD
EXTRN MODSpeed : WORD
EXTRN CurLine : WORD
EXTRN CurPattern : WORD
EXTRN ScriptPos : WORD
EXTRN MODPlaying : BYTE
EXTRN PreMODInt8 : DWORD
EXTRN MODData : DWORD
EXTRN Channels : BYTE
EXTRN ChannelInfo : BYTE
EXTRN MODFlag : BYTE
EXTRN MODVolume : WORD
EXTRN UpdateChannelWaves : BYTE
EXTRN UpdateChannelRecs : BYTE
EXTRN VoiceModes : BYTE
DATA ENDS
CODE SEGMENT WORD PUBLIC USE16
ASSUME CS:CODE,DS:DATA
PUBLIC GUS_TestBaseAddress
PUBLIC GUS_ReadVoicePos
PUBLIC GUS_Peek, GUS_Poke
PUBLIC GUS_Mem
PUBLIC GUS_SetActiveVoices
PUBLIC GUS_VoiceFreq
PUBLIC GUS_VoiceAddr
PUBLIC GUS_VoiceVolume
PUBLIC GUS_VoiceMode
PUBLIC GUS_ReadVoiceMode
PUBLIC GUS_StartVoice
PUBLIC GUS_StopVoice
PUBLIC GUS_SpeakerOn
PUBLIC GUS_SpeakerOff
PUBLIC GUS_Reset
PUBLIC GUS_VoiceBalance
PUBLIC GUS_RampRate
PUBLIC GUS_RampVolume
PUBLIC GUS_VolumeControl
PUBLIC GUS_MoveSample
PUBLIC GUS_SetClockRate
PUBLIC GUS_SetTimer
PUBLIC GUS_ResetTimer
PUBLIC GUS_SetIRQ
PUBLIC GUS_RestoreIRQ
PUBLIC MODInt8
PUBLIC GUS_StartMOD
PUBLIC GUS_ContinueMOD
PUBLIC GUS_StopMOD
PUBLIC FreqTable
PUBLIC FreqDivisors
SelectVoice MACRO
cli
mov dx,GUS_Voice
out dx,al
mov CurVoice,al
sti
ENDM
;══════════════════════════════════════════════════════════════════════
; Code segment variables local to the assembly unit
OldInt08 DD ?
OldIRQInt DD ?
OldIntFlag DW ?
OldIntCount DW ?
;══════════════════════════════════════════════════════════════════════
; Code segment tables local to the assembly unit
NoteTable DW 856,808,762,720,678,640,604,570,538,508,480,453 ; C-1 to B-1
DW 428,404,381,360,339,320,302,285,269,254,240,226 ; C-2 to B-2
DW 214,202,190,180,170,160,151,143,135,127,120,113 ; C-3 to B-3
DW 850,802,757,715,674,637,601,567,535,505,477,450 ; Finetune +1.
DW 425,401,379,357,337,318,300,284,268,253,239,225 ;
DW 213,201,189,179,169,159,150,142,134,126,119,113 ;
DW 844,796,752,709,670,632,597,563,532,502,474,447 ; Finetune +2.
DW 422,398,376,355,335,316,298,282,266,251,237,224 ;
DW 211,199,188,177,167,158,149,141,133,125,118,112 ;
DW 838,791,746,704,665,628,592,559,528,498,470,444 ; Finetune +3.
DW 419,395,373,352,332,314,296,280,264,249,235,222 ;
DW 209,198,187,176,166,157,148,140,132,125,118,111 ;
DW 832,785,741,699,660,623,588,555,524,495,467,441 ; Finetune +4.
DW 416,392,370,350,330,312,294,278,262,247,233,220 ;
DW 208,196,185,175,165,156,147,139,131,124,117,110 ;
DW 826,779,736,694,655,619,584,551,520,491,463,437 ; Finetune +5.
DW 413,390,368,347,328,309,292,276,260,245,232,219 ;
DW 206,195,184,174,164,155,146,138,130,123,116,109 ;
DW 820,774,730,689,651,614,580,547,516,487,460,434 ; Finetune +6.
DW 410,387,365,345,325,307,290,274,258,244,230,217 ;
DW 205,193,183,172,163,154,145,137,129,122,115,109 ;
DW 814,768,725,684,646,610,575,543,513,484,457,431 ; Finetune +7.
DW 407,384,363,342,323,305,288,272,256,242,228,216 ;
DW 204,192,181,171,161,152,144,136,128,121,114,108 ;
DW 907,856,808,762,720,678,640,604,570,538,504,480 ; Finetune -8.
DW 453,428,404,381,360,339,320,302,285,269,254,240 ;
DW 226,214,202,190,180,170,160,151,143,135,127,120 ;
DW 900,850,802,757,715,675,636,601,567,535,505,477 ; Finetune -7.
DW 450,425,401,379,357,337,318,300,284,268,253,238 ;
DW 225,212,200,189,179,169,159,150,142,134,126,119 ;
DW 894,844,796,752,709,670,632,597,563,532,502,474 ; Finetune -6.
DW 447,422,398,376,355,335,316,298,282,266,251,237 ;
DW 223,211,199,188,177,167,158,149,141,133,125,118 ;
DW 887,838,791,746,704,665,628,592,559,528,498,470 ; Finetune -5.
DW 444,419,395,373,352,332,314,296,280,264,249,235 ;
DW 222,209,198,187,176,166,157,148,140,132,125,118 ;
DW 881,832,785,741,699,660,623,588,555,524,494,467 ; Finetune -4.
DW 441,416,392,370,350,330,312,294,278,262,247,233 ;
DW 220,208,196,185,175,165,156,147,139,131,123,117 ;
DW 875,826,779,736,694,655,619,584,551,520,491,463 ; Finetune -3.
DW 437,413,390,368,347,338,309,292,276,260,245,232 ;
DW 219,206,195,184,174,164,155,146,138,130,123,116 ;
DW 868,820,774,730,689,651,614,580,547,516,487,460 ; Finetune -2.
DW 434,410,387,365,345,325,307,290,274,258,244,230 ;
DW 217,205,193,183,172,163,154,145,137,129,122,115 ;
DW 862,814,768,725,684,646,610,575,543,513,484,457 ; Finetune -1.
DW 431,407,384,363,342,323,305,288,272,256,242,228 ;
DW 216,203,192,181,171,161,152,144,136,128,121,114 ;
VolTable DW 00000h, 0B000h, 0B800h, 0BC00h, 0BE00h, 0C000h, 0C400h
DW 0C800h, 0CC00h, 0D000h, 0D200h, 0D400h, 0D600h, 0D800h
DW 0DA00h, 0DC00h, 0DE00h, 0E000h, 0E100h, 0E200h, 0E300h
DW 0E400h, 0E500h, 0E600h, 0E700h, 0E800h, 0E900h, 0EA00h
DW 0EB00h, 0EC00h, 0ED00h, 0EE00h, 0EF00h, 0F080h, 0F100h
DW 0F180h, 0F200h, 0F280h, 0F300h, 0F380h, 0F400h, 0F480h
DW 0F500h, 0F580h, 0F600h, 0F680h, 0F700h, 0F780h, 0F800h
DW 0F880h, 0F900h, 0F980h, 0FA00h, 0FA80h, 0FB00h, 0FB80h
DW 0FC00h, 0FC80h, 0FD00h, 0FD80h, 0FE00h, 0FE80h, 0FF00h
DW 0FF80h, 0FFF0h
; frequency divisors are used in building the frequency table and are
; for MaxVoices = 14 to MaxVoices = 32
VoiceVolumes DB 32 DUP (0)
;VoiceModes DB 32 DUP (0)
FreqDivisors DB 43, 40, 37, 35, 33, 31, 30, 28, 27, 26, 25, 24
DB 23, 22, 21, 20, 20, 19, 18
FreqTable DW 1713 DUP (0)
EffectJumps DW OFFSET DoAppregio
DW OFFSET DoSlideUp
DW OFFSET DoSlideDown
DW OFFSET DoTonePortamento
DW OFFSET DoVibrato
DW OFFSET DoToneVolSlide
DW OFFSET DoVibVolSlide
DW OFFSET DoTremolo
DW OFFSET DoNotUsed
DW OFFSET DoSetSampleOffs
DW OFFSET DoVolumeSlide
DW OFFSET DoPositionJump
DW OFFSET DoSetVolume
DW OFFSET DoPatternBreak
DW OFFSET DoExtended
DW OFFSET DoSetSpeed
; this table is set and reset during a GF1 IRQ to tell us if we've already
; serviced a voice for either a Wave Table IRQ or a Ramp IRQ
EndIRQ DB 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
DB 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
RampIRQ DB 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
DB 00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
;══════════════════════════════════════════════════════════════════════
GUS_Delay PROC
push ax
push dx
mov dx,300h
in al,dx
in al,dx
in al,dx
in al,dx
in al,dx
in al,dx
in al,dx
pop dx
pop ax
ret
GUS_Delay ENDP
;══════════════════════════════════════════════════════════════════════
; bx = rate
ClockRate PROC
push ax
push dx
cmp bx,0
jg DoDivide
xor ax,ax
jmp SetRate
DoDivide:
mov ax,65535
xor dx,dx
div bx
SetRate:
push ax
mov al,36h
out 43h,al
pop ax
out 40h,al
xchg ah,al
out 40h,al
mov cs:OldIntFlag,bx ; when this is 0, the old int is called
mov cs:OldIntCount,bx ; set oldintflag to this when reset
pop dx
pop ax
ret
ClockRate ENDP
;══════════════════════════════════════════════════════════════════════
;AX = interrupt number
;BX = offset of new interrupt handler
SetIRQInt PROC
push cx
push bx
push es
push di
cli
xor ah,ah
mov cl,al ; preserve interrupt number for use
cmp al,7
jg HighIRQ
mov di,ax ; calculate IRQ interrupt vector addx
add di,8 ; irq 0-7 = ints 8 - 0Fh
shl di,2
jmp SetIRQ
HighIRQ:
mov di,ax ; irq 8-15 = ints 70h - 77h
add di,68h ; calculate high IRQ addx - IRQ 10 = int 72h
shl di,2 ; * 4 = offset in vector table
SetIRQ:
xor ax,ax
mov es,ax
mov ax,es:[di] ; save offset of old irq handler
mov word ptr OldIRQInt,ax
mov es:[di],bx ; store offset of new irq handler
mov ax,es:[di+2] ; save segment of old irq handler
mov word ptr OldIRQInt[2],ax
mov es:[di+2],cs ; store segment of new irq handler
cmp cl,7
jle LowIRQ
sub cl,8 ; subtract 8 for the bit shift
mov dx,0A1h
jmp Enable
LowIRQ:
mov dx,21h
Enable:
mov ah,1 ; enable interrupt control mask-bit
shl ah,cl
not ah ; make the mask
in al,dx ; enable the IRQ
and al,ah
out dx,al
sti
pop di
pop es
pop bx
pop cx
ret
SetIRQInt ENDP
;════════════════════════════════════════════════════════
;al = interrupt number
RestoreIRQInt PROC
push cx
cli
mov cl,al
add al,8 ; calculate interrupt vector addx
cbw
shl al,1
shl al,1
mov di,ax
push es ; restore interrupt vector
xor ax,ax
mov es,ax
mov ax,word ptr OldIRQInt
mov es:[di],ax
mov ax,word ptr OldIRQInt[2]
mov es:[di+2],ax
pop es
mov ah,1
shl ah,cl
in al,21h
or al,ah
out 21h,al
sti
pop cx
ret
RestoreIRQInt ENDP
;══════════════════════════════════════════════════════════════════════
SetTimer PROC
push ax
push es
xor ax,ax
mov es,ax
cli
; save the old interrupt
mov ax,es:[0020h]
mov word ptr cs:OldInt08 [0],ax
mov ax,es:[0022h]
mov word ptr cs:OldInt08 [2],ax
; set the new interrupt
mov ax,OFFSET NewInt08
mov es:[0020h],ax
mov ax,cs
mov es:[0022h],ax
sti
pop es
pop ax
ret
SetTimer ENDP
;══════════════════════════════════════════════════════════════════════
ResetTimer PROC
push ax
push es
xor ax,ax
mov es,ax
cli
; restore the old interrupt
mov ax,word ptr cs:OldInt08 [0]
mov es:[0020h],ax
mov ax,word ptr cs:OldInt08 [2]
mov es:[0022h],ax
sti
pop es
pop ax
ret
ResetTimer ENDP
;══════════════════════════════════════════════════════════════════════
; BX - high byte of address, CX - low word of address
; AL - byte returned
Peek PROC
push dx
mov dx,GUS_Command
mov al,43h
out dx,al
mov dx,GUS_DataLo
mov ax,cx
out dx,ax
mov dx,GUS_Command
mov al,44h
out dx,al
mov dx,GUS_DataHi
mov al,bl
out dx,al
mov dx,GUS_DRAMIO
in al,dx
pop dx
ret
Peek ENDP
;══════════════════════════════════════════════════════════════════════
; BX - high byte of address, CX - low word of address
; AX - byte to poke
Poke PROC
push dx
push ax
mov dx,GUS_Command
mov al,43h
out dx,al
mov dx,GUS_DataLo
mov ax,cx
out dx,ax
mov dx,GUS_Command
mov al,44h
out dx,al
mov dx,GUS_DataHi
mov al,bl
out dx,al
mov dx,GUS_DRAMIO
pop ax
out dx,al
pop dx
ret
Poke ENDP
;══════════════════════════════════════════════════════════════════════
; al = number voices Ultrasound will mix (this effects playback rate)
SetActive PROC
push dx
cmp al,0Dh
jge EnoughVoices
mov al,0Dh ; minimum of 14 voices, (14 - 1)!
EnoughVoices:
cmp al,31
jle NotTooMany
mov al,31
NotTooMany:
mov ActiveVoices,al ; ActiveVoices = number active - 1
or al,0C0h ; must be OR'ed with C0h
push ax ; save the number of voices
mov dx,GUS_Command
mov al,0Eh
out dx,al ; select highest active voice function
pop ax ; pop the number of voices wanted
mov dx,GUS_DataHi
out dx,al ; now send the number of voices
pop dx
ret
SetActive ENDP
;══════════════════════════════════════════════════════════════════════
; bx = Frequency - sets for current voice (i.e. last voice selected)
VoiceFreq PROC
push ax
push bx
push cx
push dx
mov ax,bx ; put frequency in ax
movzx bx,ActiveVoices ; get frequency divisor according
sub bx,13 ; to number of voices
xor dx,dx
movzx cx,byte ptr cs:[bx + OFFSET FreqDivisors]
div cx ; divide freq in dx:ax by cx
mov bx,ax
mov dx,GUS_Command
mov al,1
out dx,al
mov dx,GUS_DataLo
mov ax,bx
out dx,ax ; send the freq number to Ultrasound
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,1
out dx,al
mov dx,GUS_DataLo
mov ax,bx
out dx,ax ; send the freq number to Ultrasound
pop dx
pop cx
pop bx
pop ax
ret
VoiceFreq ENDP
;══════════════════════════════════════════════════════════════════════
; bx = Frequency - sets for current voice (i.e. last voice selected)
NoteFreq PROC
push ax
push bx
push dx
mov dx,GUS_Command
mov al,1
out dx,al
inc dx
mov ax,bx
out dx,ax ; send the freq number to Ultrasound
pop dx
pop bx
pop ax
ret
NoteFreq ENDP
;══════════════════════════════════════════════════════════════════════
; bx:cx = start location - sets for current voice (i.e. last voice selected)
LoopStartAddr PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Command
mov al,02 ; select low address of start location
out dx,al
mov ax,cx
mov dx,bx
shrd ax,dx,7
shr dx,7
mov dx,GUS_DataLo
out dx,ax
mov dx,GUS_Command
mov al,03 ; select hi address of start location
out dx,al
mov ax,cx
mov dx,bx
shld dx,ax,9
shl ax,9
mov dx,GUS_DataLo
out dx,ax
pop dx
pop cx
pop bx
pop ax
ret
LoopStartAddr ENDP
;══════════════════════════════════════════════════════════════════════
; bx:cx = end location - sets for current voice (i.e. last voice selected)
LoopEndAddr PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Command
mov al,04 ; select low address of end location
out dx,al
mov ax,cx
mov dx,bx
shrd ax,dx,7
shr dx,7
mov dx,GUS_DataLo
out dx,ax
mov dx,GUS_Command
mov al,05 ; select hi address of start location
out dx,al
mov ax,cx
mov dx,bx
shld dx,ax,9
shl ax,9
mov dx,GUS_DataLo
out dx,ax
pop dx
pop cx
pop bx
pop ax
ret
LoopEndAddr ENDP
;══════════════════════════════════════════════════════════════════════
; bx:cx = current location in sample - sets for current voice (i.e. last voice selected)
VoiceStartAddr PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Command
mov al,0Ah ; select low address of ptr location
out dx,al
mov ax,cx
mov dx,bx
shrd ax,dx,7
shr dx,7
mov dx,GUS_DataLo
out dx,ax
mov dx,GUS_Command
mov al,0Bh ; select hi address of start location
out dx,al
mov ax,cx
mov dx,bx
shld dx,ax,9
shl ax,9
mov dx,GUS_DataLo
out dx,ax
pop dx
pop cx
pop bx
pop ax
ret
VoiceStartAddr ENDP
;══════════════════════════════════════════════════════════════════════
; bl = volume (0 thru 64) - sets for current voice (i.e. last voice selected)
; bh = 1 - save volume, bh = 0 don't save volume
TempVol DW 0
SetVol PROC FAR
push ax
push bx
push cx
push dx
mov TempVol,bx
xor bh,bh
shl bx,1 ; * 2 to address words
mov cx,cs:[bx + OFFSET VolTable]
mov dx,GUS_Command
mov al,GUS_CurVolume ; select set volume
out dx,al
mov dx,GUS_DataLo
mov ax,cx
out dx,ax ; and send it to the Ultrasound
mov cx,TempVol
cmp ch,0
je NoStoreVol
movzx bx,CurVoice
mov cs:[bx + OFFSET VoiceVolumes],cl
NoStoreVol:
pop dx
pop cx
pop bx
pop ax
ret
SetVol ENDP
;══════════════════════════════════════════════════════════════════════
; bl = volume (0 thru 64) - sets for current voice (i.e. last voice selected)
; bh = 1 - save volume, bh = 0 don't save volume
MODSetVol PROC FAR
push ax
push bx
push cx
push dx
mov TempVol,bx
cmp MODVolume,0 ; avoid a divide by zero error
je NoVolume
xor bh,bh
shl bx,1 ; * 2 to address words
sub dx,dx ; volume in dx:ax
mov ax,cs:[bx + OFFSET VolTable]
mov cx,MODVolume ; volume * MODVolume
mul cx
mov cx,100 ; (volume * MODVolume) DIV 100 = %
div cx
mov cx,ax
jmp SetTheVolumeNow
NoVolume:
mov cx,0
SetTheVolumeNow:
mov dx,GUS_Command
mov al,GUS_CurVolume ; select set volume
out dx,al
mov dx,GUS_DataLo
mov ax,cx
out dx,ax ; and send it to the Ultrasound
mov cx,TempVol
cmp ch,0
je DontStoreVol
movzx bx,CurVoice
mov cs:[bx + OFFSET VoiceVolumes],cl
DontStoreVol:
pop dx
pop cx
pop bx
pop ax
ret
MODSetVol ENDP
;══════════════════════════════════════════════════════════════════════
;ah = mode - sets for current voice (i.e. last voice selected)
VoiceMode PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Command
mov al,GUS_SetVoiceMode ; select set voice mode
out dx,al
mov dx,GUS_DataHi
mov al,ah
out dx,al
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,GUS_SetVoiceMode ; select set voice mode
out dx,al
mov dx,GUS_DataLo
mov al,ah
out dx,al
; store the voice mode
call ReadVoiceMode
movzx bx,CurVoice
mov ds:[OFFSET VoiceModes + bx],ah
pop dx
pop cx
pop bx
pop ax
ret
VoiceMode ENDP
;══════════════════════════════════════════════════════════════════════
; returns the voice mode byte in ah and al
ReadVoiceMode PROC
push dx
mov dx,GUS_Command
mov al,80h
out dx,al
xor ax,ax
mov dx,GUS_DataHi
in al,dx
mov ah,al
pop dx
ReadVoiceMode ENDP
;══════════════════════════════════════════════════════════════════════
; - sets for current voice (i.e. last voice selected)
StopRamp PROC
push dx
push ax
mov dx,GUS_Command
mov al,8Dh
out dx,al
mov dx,GUS_DataHi ; read volume control
in al,dx
or al,00000011b ; turn on ramp bits
and al,11011111b ; turn of ramp IRQ
push ax
mov dx,GUS_Command
mov al,0Dh
out dx,al
mov dx,GUS_DataHi ; set volume control
pop ax
out dx,al
pop ax
pop dx
ret
StopRamp ENDP
;══════════════════════════════════════════════════════════════════════
; - sets for current voice (i.e. last voice selected)
StartRamp PROC
push dx
push ax
mov dx,GUS_Command
mov al,8Dh
out dx,al
mov dx,GUS_DataHi ; read volume control
in al,dx
and al,11111100b ; turn off ramp bits
push ax
mov dx,GUS_Command
mov al,0Dh
out dx,al
mov dx,GUS_DataHi ; set volume control
pop ax
out dx,al
pop ax
pop dx
ret
StartRamp ENDP
;══════════════════════════════════════════════════════════════════════
; - sets for current voice (i.e. last voice selected)
StopVoice PROC
push ax
push bx
push dx
mov bx,0 ; set volume to zero to stop
; GF1 from summing
call SetVol ; & don't save voice volume
call ReadVoiceMode
or ah,00000010b ; turn on the stop voice bits
and ah,11011111b ; turn of the damn IRQ bit
call VoiceMode
pop dx
pop bx
pop ax
ret
StopVoice ENDP
;══════════════════════════════════════════════════════════════════════
; - sets for current voice (i.e. last voice selected)
StartVoice PROC
push ax
push bx
push cx
push dx
call ReadVoiceMode
and ah,11111100b
call VoiceMode
mov bx,0
mov bl,CurVoice
mov cl,cs:[OFFSET VoiceVolumes + bx]
; get the last set voice volume in bx
mov bh,0 ; bl = volume, bh = don't save
mov bl,cl
call SetVol ; reset the volume to last set vol
pop dx
pop cx
pop bx
pop ax
ret
StartVoice ENDP
;══════════════════════════════════════════════════════════════════════
; - sets for current voice (i.e. last voice selected)
MODStartVoice PROC
push ax
push bx
push cx
push dx
call ReadVoiceMode
and ah,11111100b
call VoiceMode
mov bx,0
mov bl,CurVoice
mov cl,cs:[OFFSET VoiceVolumes + bx]
; get the last set voice volume in bx
mov bh,0 ; bl = volume, bh = don't save
mov bl,cl
call MODSetVol ; reset the volume to last set vol
pop dx
pop cx
pop bx
pop ax
ret
MODStartVoice ENDP
;══════════════════════════════════════════════════════════════════════
;bx = pan value - sets for current voice (i.e. last voice selected)
VoiceBalance PROC
push ax
push dx
mov dx,GUS_Command
mov al,0Ch ; voice balance register
out dx,al
mov dx,GUS_DataHi
mov ax,bx
out dx,al
pop dx
pop ax
ret
VoiceBalance ENDP
;══════════════════════════════════════════════════════════════════════
;bl = Increment, bh = scale - sets for current voice (i.e. last voice selected)
RampRate PROC
push ax
push bx
push dx
mov dx,GUS_Command
mov al,GUS_RampVolIncr ; volume ramp rate
out dx,al
shl bh,6 ; combine the scale and
add bl,bh ; increment
mov dx,GUS_DataHi
mov al,bl
out dx,al
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,GUS_RampVolIncr ; volume ramp rate
out dx,al
mov dx,GUS_DataHi
mov al,bl
out dx,al
pop dx
pop bx
pop ax
ret
RampRate ENDP
;══════════════════════════════════════════════════════════════════════
;bx = volume start, cx = volume end - sets for current voice (i.e. last voice selected)
RampVolume PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Command
mov al,GUS_RampvolStart ; volume ramp start
out dx,al
shr bx,4 ; clip off 4 bits
mov dx,GUS_DataHi
mov al,bl
out dx,al
mov dx,GUS_Command
mov al,GUS_RampVolEnd ; volume ramp end
out dx,al
shr cx,4 ; clip off 4 bits
mov dx,GUS_DataHi
mov al,cl
out dx,al
pop dx
pop cx
pop bx
pop ax
ret
RampVolume ENDP
;══════════════════════════════════════════════════════════════════════
;bl = control byte - sets for current voice (i.e. last voice selected)
VolControl PROC
push ax
push dx
mov dx,GUS_Command
mov al,GUS_VolControl ; volume control register
out dx,al
mov dx,GUS_DataHi
mov ax,bx
out dx,al
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,GUS_VolControl ; volume control register
out dx,al
mov dx,GUS_DataHi
mov ax,bx
out dx,al
pop dx
pop ax
ret
VolControl ENDP
;══════════════════════════════════════════════════════════════════════
; returns linear voice position in dx:ax
ReadVoicePos PROC
push bx
push cx
mov dx,GUS_Command
mov al,8Ah ; read voice position
out dx,al
mov dx,GUS_DataLo
in ax,dx ; Low voice position
mov cx,ax ; save it
mov dx,GUS_Command
mov al,8Bh
out dx,al
mov dx,GUS_DataLo
in ax,dx
; convert the position to a linear address
xor dx,dx
mov bx,cx
shl cx,7
shl dx,7
shr bx,9
or dx,bx
shr ax,9
and ax,7Fh
or cx,ax
mov ax,cx
pop cx
pop bx
ret
ReadVoicePos ENDP
;══════════════════════════════════════════════════════════════════════
Reset PROC
push ax
push bx
push cx
push dx
mov dx,GUS_Base
mov al,01001010b ; turn off speaker and enable IRQs
out dx,al
mov dx,GUS_Command
mov al,04Ch
out dx,al
mov dx,GUS_DataHi
mov al,0
out dx,al
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,4Ch ; initialization register
out dx,al
mov dx,GUS_DataHi
mov al,00000111b ; turn initialization off so
out dx,al ; that we can write to the card
call GUS_Delay
call GUS_Delay
mov dx,GUS_Command
mov al,41h ; DMA control register
out dx,al
mov dx,GUS_DataHi
xor al,al ; turn off all pending DMA IRQs
out dx,al
mov dx,GUS_Command
mov al,45h ; Timer control register
out dx,al
mov dx,GUS_DataHi
xor al,al ; clear all pending timer IRQs
out dx,al
mov dx,GUS_Command
mov al,49h ; Sample control IRQs
out dx,al
mov dx,GUS_DataHi
xor al,al ; clear all pending sample control IRQs
out dx,al
mov dx,GUS_Command
mov al,0Eh ; active voices register
out dx,al
mov dx,GUS_DataHi
mov al,31
mov ActiveVoices,31
or al,0Ch ; select number of active voices
out dx,al
; read a few ports (anyone know why? I don't.)
mov dx,GUS_Status
in al,dx
mov dx,GUS_Command
mov al,41h ; DMA control register
out dx,al
mov dx,GUS_DataHi ; read DMA control register
in al,dx
mov dx,GUS_Command
mov al,49h ; Sample Control IRQ register
out dx,al
mov dx,GUS_DataHi
in al,dx ; read Sample Control register
mov dx,GUS_Command
mov al,8Fh ; IRQ status register
out dx,al
mov dx,GUS_DataHi
in al,dx
; turn all voices and ramps off
mov cx,0
VoiceClearLoop:
mov al,cl
SelectVoice
; mov dx,GUS_Voice
; mov al,cl ; select voice
; out dx,al
; inc dx ; GUS_Command
; mov al,0 ; select Voice Mode register
; out dx,al
; add dx,2 ; GUS_DataHi
; mov al,3 ; voice off, no IRQs enabled
; out dx,al
; xor bx,bx
; mov bl,cl
; mov ds:[OFFSET VoiceModes + bx],3
mov ah,3 ; voice off, no IRQs, no Loop
call VoiceMode ; set the voice mode register
; and store the value
mov bx,0100h ; set voice volume register
call SetVol ; and store the value
inc dx
sub dx,2 ; GUS_Command
mov al,0Dh ; volume control register
out dx,al
add dx,2 ; GUS_DataHi
mov al,3 ; ramp off
out dx,al
sub dx,2 ; GUS_Command
mov al,0Ch ; voice balance control
out dx,al
add dx,2 ; GUS_DataHi
mov al,7 ; center balance
out dx,al
inc cx ; next voice
cmp cx,32
jne VoiceClearLoop
; read a few ports again (still dont know why.)
mov dx,GUS_Command
mov al,41h ; DMA control register
out dx,al
mov dx,GUS_DataHi ; read DMA control register
in al,dx
mov dx,GUS_Command
mov al,49h ; Sample Control IRQ register
out dx,al
mov dx,GUS_DataHi
in al,dx ; read Sample Control register
mov dx,GUS_Command
mov al,8Fh ; IRQ status register
out dx,al
mov dx,GUS_DataHi
in al,dx
mov dx,GUS_Command
mov al,4Ch ; init mode register
out dx,al
mov dx,GUS_DataHi
mov al,7 ; set first 3 bits
out dx,al ; return to init mode
mov dx,GUS_Base
mov al,01001101b ; turn on speaker and enable IRQs
; line in and mic in = off
out dx,al
mov GUS_Mixer,al ; save the mixer val since we can't
; read it ever
pop dx
pop cx
pop bx
pop ax
ret
Reset ENDP
;══════════════════════════════════════════════════════════════════════
GUS_ReadVoicePos PROC FAR
VoiceNum EQU byte ptr [bp+06]
push bp
mov bp,sp
mov al,VoiceNum
SelectVoice
call ReadVoicePos
mov sp,bp
pop bp
ret 2
GUS_ReadVoicePos ENDP
;══════════════════════════════════════════════════════════════════════
GUS_Peek PROC FAR
HiAddr1 EQU word ptr [bp+08]
LoAddr1 EQU word ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitPeek
mov cx,LoAddr1
mov bx,HiAddr1
call Peek
ExitPeek:
mov bp,sp
pop bp
ret 4
GUS_Peek ENDP
;══════════════════════════════════════════════════════════════════════
GUS_Poke PROC FAR
HiAddr2 EQU word ptr [bp+10]
LoAddr2 EQU word ptr [bp+08]
Value EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitPoke
mov cx,LoAddr2
mov bx,HiAddr2
movzx ax,Value
call Poke
ExitPoke:
mov sp,bp
pop bp
ret 6
GUS_Poke ENDP
;══════════════════════════════════════════════════════════════════════
GUS_TestBaseAddress PROC FAR
push bp
mov bp,sp
mov dx,GUS_Command
mov al,4Ch
out dx,al
mov dx,GUS_DataHi
mov al,01
out dx,al ; turn OFF Ultrasound reset state
call GUS_Delay
call GUS_Delay
mov ax,0AAh
mov bx,0
mov cx,0
call Poke
mov al,055h
mov bx,01h
call Poke
mov bx,0
call Peek
push ax ; save the peeked value
mov dx,GUS_Command
mov al,4Ch
out dx,al ; turn Ultrasound init state ON
mov dx,GUS_DataHi
mov al,00
out dx,al
pop ax ; restore the peeked value
cmp al,0AAh
jne GUS_NotFound
mov ax,1
jmp GUS_TestExit
GUS_NotFound:
xor ax,ax
GUS_TestExit:
mov dx,GUS_Command
mov al,4Ch
out dx,al ; turn Ultrasound init state OFF
mov dx,GUS_DataHi
mov al,01
out dx,al
mov sp,bp
pop bp
ret
GUS_TestBaseAddress ENDP
;══════════════════════════════════════════════════════════════════════
GUS_Mem PROC FAR
push bp
mov bp,sp
xor dx,dx
cmp GUS_Base,210
jl ExitMem
xor cx,cx
mov bx,4
mov ax,0AAh
call Poke
xor cx,cx
mov bx,4
call Peek
mov dx,0100h
cmp al,0AAh
jne ExitMem
xor cx,cx
mov bx,8
mov ax,0AAh
call Poke
xor cx,cx
mov bx,8
call Peek
mov dx,0200h
cmp al,0AAh
jne ExitMem
xor cx,cx
mov bx,0Ch
mov al,0AAh
call Poke
xor cx,cx
mov bx,0Ch
call Peek
mov dx,0300h
cmp al,0AAh
jne ExitMem
mov dx,0400h
ExitMem:
mov ax,dx
mov sp,bp
pop bp
ret
GUS_Mem ENDP
;══════════════════════════════════════════════════════════════════════
;moves the sample to GUS memory
;es:di - address of sample in DOS memory
;bx:cx = address in GUS memory to store sample
;dx = sample length
MoveSample PROC
StoreLoop:
push dx ; save the sample length counter
mov dx,GUS_Command
mov al,43h
out dx,al
inc dx ; GUS_DataLo
mov ax,cx
out dx,ax
dec dx ; GUS_Command
mov al,44h
out dx,al
add dx,2 ; GUS_DataHi
mov al,bl
out dx,al
add dx,2 ; GUS_DRAMIO
mov al,es:[di] ; load the byte to send
inc di
out dx,al
add cx,1
adc bx,0 ; increment the GUS memory ptr
pop dx
dec dx
jnz StoreLoop ; do it again if we're not at the end
ret
MoveSample ENDP
;══════════════════════════════════════════════════════════════════════
; PROCEDURE GUS_VoiceFreq (VoiceNum : BYTE; Hertz : WORD);
GUS_VoiceFreq PROC FAR
Voice1 EQU byte ptr [bp+08]
Freq1 EQU word ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVoiceFreq
mov al,Voice1
SelectVoice
mov bx,Freq1
call VoiceFreq
ExitVoiceFreq:
mov sp,bp
pop bp
ret 4
GUS_VoiceFreq ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_ActiveVoices (Voices : BYTE);
GUS_SetActiveVoices PROC FAR
Voice2 EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitActive
movzx ax,Voice2
call SetActive
ExitActive:
mov sp,bp
pop bp
ret 2
GUS_SetActiveVoices ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_VoiceAddr (Voice : BYTE; Start, CurPtr, End : LONGINT);
GUS_VoiceAddr PROC FAR
Voice3 EQU byte ptr [bp+18]
PtrHi EQU word ptr [bp+16]
PtrLo EQU word ptr [bp+14]
StartHi EQU word ptr [bp+12]
StartLo EQU word ptr [bp+10]
EndHi EQU word ptr [bp+08]
EndLo EQU word ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVoiceAddr
mov al,Voice3
SelectVoice
;mov ax,Voice3
mov bx,PtrHi
mov cx,PtrLo
call VoiceStartAddr
;mov ax,Voice3
mov bx,StartHi
mov cx,StartLo
call LoopStartAddr
;mov ax,Voice3
mov bx,EndHi
mov cx,EndLo
call LoopEndAddr
ExitVoiceAddr:
mov sp,bp
pop bp
ret 14
GUS_VoiceAddr ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_VoiceVolume (Voice : BYTE; Volume : WORD);
GUS_VoiceVolume PROC FAR
Voice4 EQU byte ptr [bp+08]
Volume EQU word ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVoiceVol
mov al,Voice4
SelectVoice
mov bx,Volume
mov bh,1 ; save volume!
call SetVol
ExitVoiceVol:
mov sp,bp
pop bp
ret 4
GUS_VoiceVolume ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_VoiceMode (Voice : BYTE; Mode : BYTE);
GUS_VoiceMode PROC FAR
Voice5 EQU byte ptr [bp+08]
Mode EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVoiceMode
mov al,Voice5
SelectVoice
mov ah,Mode
call VoiceMode
ExitVoiceMode:
mov sp,bp
pop bp
ret 4
GUS_VoiceMode ENDP
;══════════════════════════════════════════════════════════════════════
;FUNCTION GUS_ReadVoiceMode (Voice : BYTE): BYTE;
GUS_ReadVoiceMode PROC FAR
Voice12 EQU byte ptr [bp+06]
push bp
mov bp,sp
mov al,Voice12
SelectVoice
call ReadVoiceMode
mov sp,bp
pop bp
ret 2
GUS_ReadVoiceMode ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_StopVoice (Voice : BYTE);
GUS_StopVoice PROC FAR
Voice6 EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitStopVoice
mov al,Voice6
SelectVoice
call StopVoice
ExitStopVoice:
mov sp,bp
pop bp
ret 2
GUS_StopVoice ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_StartVoice (Voice : BYTE);
GUS_StartVoice PROC FAR
Voice7 EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitStartVoice
mov al,Voice7
SelectVoice
call StartVoice
ExitStartVoice:
mov sp,bp
pop bp
ret 2
GUS_StartVoice ENDP
;══════════════════════════════════════════════════════════════════════
GUS_SpeakerOn PROC FAR
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitSpeakerOn
mov dx,GUS_Base
mov al,GUS_Mixer
or al,00001000b ; make sure latches are on!
and al,11111101b ; turn speaker on
mov GUS_Mixer,al ; store the current value
out dx,al
ExitSpeakerOn:
mov sp,bp
pop bp
ret
GUS_SpeakerOn ENDP
;══════════════════════════════════════════════════════════════════════
GUS_SpeakerOff PROC FAR
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitSpeakerOff
mov dx,GUS_Base
mov al,GUS_Mixer
or al,00001010b ; turn on latches and turn speaker off
mov GUS_Mixer,al ; store the current value
out dx,al
ExitSpeakerOff:
mov sp,bp
pop bp
ret
GUS_SpeakerOff ENDP
;══════════════════════════════════════════════════════════════════════
GUS_Reset PROC FAR
cmp GUS_Base,210
jl ExitReset
call Reset
ExitReset:
ret
GUS_Reset ENDP
;══════════════════════════════════════════════════════════════════════
GUS_VoiceBalance PROC FAR
Voice8 EQU byte ptr [bp+08]
Balance EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVoiceBalance
mov al,Voice8
SelectVoice
movzx bx,Balance
call VoiceBalance
ExitVoiceBalance:
mov sp,bp
pop bp
ret 4
GUS_VoiceBalance ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_RampRate (Voice, Increment, Scale : BYTE);
GUS_RampRate PROC FAR
Voice9 EQU byte ptr [bp+10]
Incr EQU byte ptr [bp+08]
Scale EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitRampRate
mov al,Voice9
SelectVoice
mov bl,Incr
mov bh,Scale
call RampRate
ExitRampRate:
mov sp,bp
pop bp
ret 6
GUS_RampRate ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_RampVolume (Voice, StartVol, EndVol : BYTE);
GUS_RampVolume PROC FAR
Voice10 EQU byte ptr [bp+10]
SVol EQU byte ptr [bp+08]
EVol EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitRampVol
mov al,Voice10
SelectVoice
xor bx,bx
mov bl,EVol
shl bx,1
mov cx,cs:[bx + OFFSET VolTable]
movzx bx,SVol
shl bx,1
mov ax,cs:[bx + OFFSET VolTable]
mov bx,ax
call RampVolume
ExitRampVol:
mov sp,bp
pop bp
ret 6
GUS_RampVolume ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_VolumeControl (Voice, Control : BYTE);
GUS_VolumeControl PROC FAR
Voice11 EQU byte ptr [bp+08]
Control EQU byte ptr [bp+06]
push bp
mov bp,sp
cmp GUS_Base,210
jl ExitVolumeCon
mov al,Voice11
SelectVoice
movzx bx,Control
call VolControl
ExitVolumeCon:
mov sp,bp
pop bp
ret 4
GUS_VolumeControl ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_StoreSample (DOSAddr, GUSAddr : LONGINT; Len : WORD);
GUS_MoveSample PROC FAR
DOSAddrHi EQU word ptr [bp+14]
DOSAddrLo EQU word ptr [bp+12]
GUSAddrHi EQU word ptr [bp+10]
GUSAddrLo EQU word ptr [bp+08]
Len EQU word ptr [bp+06]
push bp
mov bp,sp
mov ax,DOSAddrHi
mov es,ax
mov di,DOSAddrLo
mov bx,GUSAddrHi
mov cx,GUSAddrLo
mov dx,Len
call MoveSample
mov sp,bp
pop bp
ret 10
GUS_MoveSample ENDP
;══════════════════════════════════════════════════════════════════════
;PROCEDURE GUS_SetClockRate (rate : WORD);
GUS_SetClockRate PROC FAR
Rate EQU word ptr [BP+06]
push bp
mov bp,sp
mov bx,Rate
call ClockRate
mov sp,bp
pop bp
ret 2
GUS_SetClockRate ENDP
;══════════════════════════════════════════════════════════════════════
GUS_SetTimer PROC FAR
push bp
mov bp,sp
call SetTimer
mov sp,bp
pop bp
ret
GUS_SetTimer ENDP
;══════════════════════════════════════════════════════════════════════
GUS_ResetTimer PROC FAR
push bp
mov bp,sp
call ResetTimer
mov sp,bp
pop bp
ret
GUS_ResetTimer ENDP
;══════════════════════════════════════════════════════════════════════
GUS_SetIRQ PROC FAR
push bp
mov bp,sp
mov ax,GUS_IRQ
mov bx,OFFSET IRQInt
call SetIRQInt
; al will store the GF1 IRQ number in bits 0-2
cmp GUS_IRQ,2
jne TestIRQ5
mov al,1
jmp SetIRQCon
TestIRQ5:
cmp GUS_IRQ,5
jne TestIRQ3
mov al,2
jmp SetIRQCon
TestIRQ3:
cmp GUS_IRQ,3
jne TestIRQ7
mov al,3
jmp SetIRQCon
TestIRQ7:
cmp GUS_IRQ,7
jne TestIRQ11
mov al,4
jmp SetIRQCon
TestIRQ11:
cmp GUS_IRQ,11
jne TestIRQ12
mov al,5
jmp SetIRQCon
TestIRQ12:
cmp GUS_IRQ,12
jne TestIRQ15
mov al,6
jmp SetIRQCon
TestIRQ15:
cmp GUS_IRQ,15
jne NoIRQ
mov al,7
jmp SetIRQCon
NoIRQ:
xor al,al
SetIRQCon:
push ax ; save the IRQ bit
; mov dx,GUS_Base
; mov al,GUS_Mixer
; or al,01000000b ; select IRQ Control Register
; mov GUS_Mixer,al
; out dx,al
pop ax ; restore the IRQ bit
; mov dx,GUS_IRQDMACon ; IRQ/DMA Control register
; out dx,al ; send the IRQ control bits
mov sp,bp
pop bp
ret
GUS_SetIRQ ENDP
;══════════════════════════════════════════════════════════════════════
GUS_RestoreIRQ PROC FAR
push bp
mov bp,sp
mov ax,GUS_IRQ
call RestoreIRQInt
mov sp,bp
pop bp
ret
GUS_RestoreIRQ ENDP
;══════════════════════════════════════════════════════════════════════
NewInt08 PROC FAR
push ax
pushf
mov al,20h ; enable interrupts
out 20h,al
sti
dec cs:OldIntFlag
cmp cs:OldIntFlag,0
jne ExitNewInt
mov ax,cs:OldIntCount ; reset the counter
mov cs:OldIntFlag,ax
popf
pop ax
pushf
call OldInt08
iret
ExitNewInt:
popf
pop ax
iret
NewInt08 ENDP
;══════════════════════════════════════════════════════════════════════
Mess1 DB 'WTirq ',0
Mess2 DB 'Rirq ',0
Mess3 DB 'CurVoice = ',0
IRQInt PROC FAR
push ax
push bx
push cx
push dx
push ds
push es
push si
push di
pushf
mov ax,SEG Data ; set DS to TP's DS
mov ds,ax
mov di,OFFSET EndIRQ
mov si,OFFSET RampIRQ
mov al,CurVoice
push ax
CheckForIRQ:
mov dx,GUS_Command
mov al,08Fh ; select read IRQ source register
out dx,al
add dx,2 ; GUS_DataLow
in al,dx ; get the number of voice sending IRQ
; plus the type of IRQ
mov cl,al
mov ch,cl
movzx bx,al ; IRQ status in cl and bl
; call PrintHexByte ; print register contents
and cl,10000000b ; isolate WaveIRQ bit
and ch,01000000b ; isolate RampIRQ bit
and bl,00011111b ; remove bits 7-5 to get voice #
or cl,cl
jnz NextTest ; if set, no wavetable IRQ
jmp WaveTableIRQ
NextTest:
or ch,ch
jnz IRQExit ; if set then no Ramp IRQ either, exit
jmp FixRampIRQ ; not set, so fix Ramp IRQ
WaveTableIRQ:
cmp byte ptr cs:[di + bx],0; check table to see if we've
jne AlreadyServiced ; serviced this voice already
; if yes then check for Ramp IRQs
mov byte ptr cs:[di + bx],1; put 1 in table so we'll know
; we've already serviced it
; mov al,0FFh
; call PrintHexByte ; print register contents
mov al,bl ; get voice number in al
SelectVoice ; change to voice #
; turn the voice off so it will stop generating IRQs
call StopVoice
; set the voice pointer to start location
jmp TestForRamp
AlreadyServiced:
; mov al,0FDh
; call PrintHexByte ; print register contents
; test to see if there is a ramp IRQ
TestForRamp:
or ch,ch
jnz CheckForIRQ ; no ramp IRQ so check for another
; IRQ
FixRampIRQ:
; stop the ramp from generating an IRQ
cmp byte ptr cs:[si + bx],0; check table to see if we've
jne CheckForIRQ ; serviced this voice already
; if yes then check for other IRQs
mov byte ptr cs:[si + bx],1; put 1 in table so we'll know
; we've already serviced it
mov al,0FEh
call PrintHexByte ; print register contents
mov al,bl
SelectVoice
call StopRamp
jmp CheckForIRQ ; check for IRQ's on other voices
IRQExit:
; mov al,07Fh
; call PrintHexByte ; print register contents
mov ax,cs
mov es,ax
mov di,OFFSET EndIRQ ; zero tables before leaving int
mov cx,32 ; 64 bytes / 2 = 32
xor ax,ax ; store a zero
rep stosw
mov ax,GUS_IRQ
cmp ax,07
jg HiIRQ
mov al,20h ; clear primary PIC
out 20h,al
HiIRQ:
mov al,20h
out 0A0h,al ; clear secondary PIC
pop ax ; restore old current voice
SelectVoice
IRQDone:
popf
pop di
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
pushf
call OldIRQInt
iret
IRQInt ENDP
;══════════════════════════════════════════════════════════════════════
; al = bits 0-3 nibble to print
CurX DB 0
CurY DB 0
CurAttr DB 1Fh
OldX DB 0
OldY DB 0
PrintNibble PROC
push ax
push bx
push cx
push dx
cmp cs:[CurX],72
jle NoChangeX
mov cs:[CurX],0
NoChangeX:
push ax
mov ah,02h
xor bh,bh
mov dh,cs:[CurY]
mov dl,cs:[CurX]
int 10h
pop ax
and al,00001111b ; clear the top four bits of byte
cmp al,0Ah
jge IsLetter
add al,30h ; add 48d to get ASCII digit
jmp PrintIt
IsLetter:
add al,37h ; add 55d to get ASCII character
PrintIt:
mov ah,0Eh
xor bx,bx
int 10h ; output one hex digit
inc cs:[CurX]
pop dx
pop cx
pop bx
pop ax
ret
PrintNibble ENDP
;══════════════════════════════════════════════════════════════════════
; al = byte to print in hex
PrintHexByte PROC
push ax
push bx
push cx
mov ah,al
shr al,4
call PrintNibble
mov al,ah
call PrintNibble
mov ax,0E20h
xor bx,bx
int 10h ; output a space
inc cs:[CurX]
pop cx
pop bx
pop ax
ret
PrintHexByte ENDP
;══════════════════════════════════════════════════════════════════════
; al = byte to print in binary
PrintBinByte PROC
push ax
push bx
push cx
push dx
cmp cs:[CurX],72
jle LeaveX
mov cs:[CurX],0
LeaveX:
mov ah,03h ; get cursor position
xor bh,bh
int 10h
mov OldX,dl ; save cursor position
mov OldY,dh
mov dx,ax
mov cx,9
BinLoop1:
rcl dl,1
jc Print1
mov al,30h
jmp PrintOneBit
Print1:
mov al,31h
PrintOneBit:
push ax
mov ah,02h
xor bh,bh
mov dh,cs:[CurY]
mov dl,cs:[CurX]
int 10h
inc cs:[CurX]
pop ax
push cx
mov ah,09h
movzx bx,CurAttr
mov cx,1
int 10h
pop cx
loop BinLoop1
mov ax,0E20h
mov bx,16
int 10h ; output a space
inc cs:[CurX]
mov ah,02h ; restore old cursor position
xor bh,bh
mov dh,OldY
mov dl,OldX
int 10h
add CurAttr,10h
cmp CurAttr,08Fh
jne NoReset
mov CurAttr,1Fh
NoReset:
pop dx
pop cx
pop bx
pop ax
ret
PrintBinByte ENDP
;══════════════════════════════════════════════════════════════════════
;si = offset of string
Write PROC
push ax
push bx
push dx
push si
CheckCursor:
cmp cs:[CurX],60
jle MoveCursor
mov cs:[CurX],0
MoveCursor:
mov ah,02h
xor bh,bh
mov dh,cs:[CurY]
mov dl,cs:[CurX]
int 10h
WriteLoop:
mov al,cs:[si]
cmp al,0
je ExitWrite
mov ah,0Eh
mov bx,16
int 10h
inc si
inc cs:[CurX]
jmp WriteLoop
ExitWrite:
pop si
pop dx
pop bx
pop ax
ret
Write ENDP
;══════════════════════════════════════════════════════════════════════
MaxTrax DB 0
DataPos DD 0
RecCnt DB 0
UpdateChanRecs PROC
inc RecCnt
cmp RecCnt,2
jge DoUpdate
ret
DoUpdate:
mov RecCnt,0
les di,[MODData] ; segment:offset of MOD data
mov al,es:[di + 2011] ; number of channels
mov MaxTrax,al
mov cl,0
mov bx,OFFSET ChannelInfo
UpdateLoop0:
mov al,cl
SelectVoice
cmp byte ptr ds:[bx],0
je VoiceIsOff
mov al,cl
call ReadVoiceMode ; get the voice mode byte in ah and al
test al,1 ; if voice is stopped don't jump
jz VoiceIsPlaying
VoiceIsOff:
; store 0 in Channel Volume
inc bx
mov byte ptr ds:[bx],0
; store 0 in Channel Hit
inc bx
mov byte ptr ds:[bx],0
add bx,81 ; move bx to next channel record
inc cl
cmp cl,MaxTrax
jl UpdateLoop0
ret
VoiceIsPlaying:
; call ReadVoicePos
; push bx
; push cx
; mov bx,dx
; mov cx,ax
; mov word ptr cs:[DataPos],cx
; mov word ptr cs:[DataPos + 2],bx
; call Peek
; pop cx
; pop bx
inc bx
mov byte ptr ds:[bx],0 ; put "volume" in ChannelVolume
inc bx
cmp byte ptr ds:[bx],0
je HitIsZero
dec byte ptr ds:[bx] ; decrement ChannelHit counter
HitIsZero:
add bx,81 ; point to Wave array
inc cl
cmp cl,MaxTrax
jl UpdateLoop0
ret
UpdateChanRecs ENDP
;══════════════════════════════════════════════════════════════════════
UpdateWaves PROC
mov cl,0
mov bx,OFFSET ChannelInfo
UpdateLoop1:
mov al,cl
SelectVoice
cmp byte ptr ds:[bx],0
je WaveVoiceIsOff
mov al,cl
call ReadVoiceMode ; get the voice mode byte in ah and al
test al,1 ; if voice is stopped don't jump
jz WaveVoiceIsPlaying
WaveVoiceIsOff:
add bx,3
push cx ; store a flat line in this voice's
; wave or we'll see data that's after
; the end of the current instrument
cld
mov ax,ds
mov es,ax
mov di,bx
mov eax,0
mov cx,20
rep stosd
pop cx
add bx,80 ; move bx to next channel record
inc cl
cmp cl,4
jl UpdateLoop1
ret
WaveVoiceIsPlaying:
call ReadVoicePos
push bx
push cx
mov bx,dx
mov cx,ax
mov word ptr cs:[DataPos],cx
mov word ptr cs:[DataPos + 2],bx
pop cx
pop bx
add bx,3 ; point to Wave array
push cx
mov cx,80
WaveLoop:
mov dx,GUS_Command
mov al,43h
out dx,al
mov dx,GUS_DataLo
mov ax,word ptr cs:[DataPos]
out dx,ax
mov dx,GUS_Command
mov al,44h
out dx,al
mov dx,GUS_DataHi
mov al,byte ptr cs:[DataPos + 2]
out dx,al
mov dx,GUS_DRAMIO
in al,dx
inc DataPos
mov byte ptr ds:[bx],al
inc bx
dec cx
jnz WaveLoop
pop cx
inc cl
cmp cl,4
jl UpdateLoop1
ret
UpdateWaves ENDP
;══════════════════════════════════════════════════════════════════════
MajorTick DW 0 ; tick occurs every 20 of these
TickCnt DW 0 ; tracks the time till next pat line
WaveTick DW 0 ; tracks occurance of wave updates
OldCnt DW 0 ; keeps the original int 08 interrupt time
LineOfs DW 0
CurChannel DB 0 ; current channel being processed
LastVoice DB 0 ; save variable for CurVoice
NumPats DW 0
EndJumpPos DW 0
JumpToPat DW 0FFh ; pattern to jump to after cur line
JumpToLine DW 0FFh ; line to jump to in pattern
ChannelVol DB 41h
;══════════════════════════════════════════════════════════════════════
; handles all the timing for playing the current MOD file
; expects the int 08 interrupt rate to be 1001 times per second (55 * 18.2)
; OrigRate is the rate that the old interrupt 8 was called
;══════════════════════════════════════════════════════════════════════
MODInt8 PROC FAR
push ax ; save original registers
push bx
push cx
push dx
push ds
push es
push si
push di
push gs
pushf ; save original flags
mov ax,DATA
mov ds,ax
mov al,CurVoice
mov LastVoice,al
cmp MODPlaying,0
jne CheckTick ; if mod is playing then jump
jmp NoMODTick
; mod player stuff starts here
CheckTick:
call UpdateChanRecs
inc MajorTick ; this makes sure a 'tick' happens
cmp MajorTick,19 ; only 50 times per second
jge TickOccurred
jmp NoMODTick
TickOccurred:
cmp UpdateChannelWaves,0
je NoWaveUpdate
inc WaveTick
cmp WaveTick,3 ; wave updates only 16 time / sec
jl NoWaveUpdate ; cause Gravis I/O is slow
mov WaveTick,0
call UpdateWaves
NoWaveUpdate:
mov MajorTick,0
inc TickCnt
mov ax,TickCnt
cmp ax,MODSpeed ; if TickCount = MODSpeed then
jge DoSomething
jmp NoMODTick
DoSomething:
mov TickCnt,0
; begin processing the current line of the mod
mov CurChannel,0
les di,[MODData] ; segment:offset of MOD data
mov bx,CurPattern
shl bx,2 ; curpattern * 4
add bx,di ; offset to PatternPtr
add bx,PatternOfs ; skip over instrument info
mov si,word ptr es:[bx] ; offset of current pattern data
mov ax,es:[bx + 2] ; segment of current pattern data
mov gs,ax ; gs:si seg:ofs of cur pattern data
mov ax,PatLineSize
mul CurLine
add si,ax ; si = offset of current pattern line
mov LineOfs,si
ProcessChannel:
mov al,CurChannel ; al = voice number
SelectVoice ; select voice number 0-maxtracks - 1
ProcessEffect:
mov si,LineOfs ; restore offset to cur channel's note
mov ChannelVol,65 ; flag\var for volume changes
mov bx,gs:[si + 3] ; effect number in bl, arg in bh
mov cl,bh ; effect arg in cl
xor bh,bh ; now bx will address jump table
cmp bl,0
jne GoToEffect
CheckAppregio:
cmp cl,0 ; appregio only if arg is <> 0
jne GoToEffect
jmp PlayNote
; come here ONLY if there is an effect to be played
GoToEffect:
shl bx,1
jmp word ptr EffectJumps [bx]
DoAppregio:
jmp PlayNote
DoSlideUp:
jmp PlayNote
DoSlideDown:
jmp PlayNote
DoTonePortamento:
jmp PlayNote
DoVibrato:
jmp PlayNote
DoToneVolSlide:
jmp PlayNote
DoVibVolSlide:
jmp PlayNote
DoTremolo:
jmp PlayNote
DoNotUsed:
jmp PlayNote
DoSetSampleOffs:
jmp PlayNote
DoVolumeSlide:
jmp PlayNote
DoPositionJump:
xor ch,ch
mov JumpToPat,cx
mov CurLine,63 ; forces a pattern change after line
jmp PlayNote
DoSetVolume:
cmp cl,64
ja DoSetFlag
mov ChannelVol,cl
mov bh,01
mov bl,cl
call MODSetVol
; set ChannelHit in channel Record, this variable is used to do spectrum
; analyzer bar stuff, basically
shl cl,1
mov bl,CurChannel
mov ax,ChannelInfoSize
mul bl
mov bx,ax
mov byte ptr ds:[OFFSET ChannelInfo + 2 + bx],cl
jmp PlayNote
DoSetFlag:
sub cl,65 ; subtract 65 from effect arg
mov MODFlag,cl ; and store it in MODFlag
jmp PlayNote
DoPatternBreak:
mov bl,cl
shr bl,4
mov ax,10
mul bl ; ax = 10 * upper nibble of effect arg
and cl,00001111b ; cx = zeroed upper nibble of effect arg
xor ch,ch
add cl,al ; cl = (10 * upper nibble) + (lower nibble)
mov JumpToLine,cx ; line to jump to in next pattern
mov CurLine,63 ; forces a pattern change after line
jmp PlayNote
DoExtended:
jmp PlayNote
DoSetSpeed:
xor ch,ch
mov MODSpeed,cx
PlayNote:
; determine if this channel is on or off
movzx bx,CurChannel
mov ax,ChannelInfoSize
mul bl
mov bx,ax
cmp byte ptr ds:[OFFSET ChannelInfo + bx],0
jne ChannelIsOn
jmp NextChannel
ChannelIsOn:
mov si,LineOfs ; restore offset to cur channel's note
mov dl, byte ptr gs:[si]
cmp dl,0 ; dl = instrument number + 1
je NextChannel
; determine voice frequency
call StopVoice
mov bx,gs:[si + 1] ; voice period in bx
shl bx,1
mov ax,cs:[OFFSET FreqTable + bx]
mov bx,ax
call NoteFreq
movzx cx, byte ptr gs:[si]
dec cl ; cl = instrument #
mov ax,44
mul cl
mov si,ax ; offset to instrument record
add si,di ; is now in si
mov ebx,es:[si] ; eax = location of sample in GUS RAM
mov cx,bx
shr ebx,16 ; bx:cx = longint sample location
call VoiceStartAddr ; set current address of voice
mov dx,word ptr es:[si + 19]; repeat length
cmp dx,2
jg LoopInstr
; set up the voice for no looping (i.e. play entire sample only once)
call LoopStartAddr
mov eax,es:[si + 9] ; eax = length of sample
mov ebx,es:[si] ; ebx = location of sample in GUS RAM
dec eax ; subtract one from length
add ebx,eax ; add length to location
mov cx,bx
shr ebx,16 ; bx:cx = end address of voice
call LoopEndAddr ; set end address of voice
jmp StartSample
LoopInstr:
mov eax,es:[si] ; eax = location of sample in GUS RAM
movzx ebx,word ptr es:[si + 17]; bx = loop start addres DIV 2
shl ebx,1 ; * 2 for loop start
add ebx,eax ; ebx = loop start in GUS RAM
push ebx
mov cx,bx ; put lower offset in cx
shr ebx,16 ; shift upper into bx
call LoopStartAddr ; set start address of voice
movzx ebx,word ptr es:[si + 19]; bx = length of loop DIV 2
shl ebx,1 ; * 2 for loop length
dec ebx ;
pop eax
add ebx,eax ; eax = loop start + loop length
mov cx,bx
shr ebx,16 ; bx:cx = loop end location
call LoopEndAddr ; set end address of voice
StartSample:
; is this sample's data length = 0 bytes?
cmp dword ptr es:[si + 9],0
jne InstrumentNotDummy
; sample is 0 bytes, so set volume to 0 and stop the voice
call StopVoice
jmp NextChannel
InstrumentNotDummy:
; set ChannelHit in channel Record, this variable is used to do spectrum
; analyzer bar stuff, basically
; mov bl,CurChannel
; mov ax,83
; mul bl
; mov bx,ax
; mov byte ptr ds:[OFFSET ChannelInfo + 2 + bx],127
; set voice mode, start playing
mov ah,00000000b
mov bx,word ptr es:[si + 19]; repeat length
cmp bx,2
jle IRQBitSet ; if no looping then jump
mov ah,GUS_Loop ; if looping, then turn off IRQs
; for this channel
IRQBitSet:
call VoiceMode
cmp ChannelVol,65
jne VolumeAlreadySet ; volume was already set by an
; effect so skip volume set here
mov bh,1
mov bl,byte ptr es:[si + 16]; bl = sample's default volume
call MODSetVol
; set ChannelHit in channel Record, this variable is used to do spectrum
; analyzer bar stuff, basically
mov cl,bl
shl cl,1
mov bl,CurChannel
mov ax,83
mul bl
mov bx,ax
mov byte ptr ds:[OFFSET ChannelInfo + 2 + bx],cl
VolumeAlreadySet:
; start the voice playing
call MODStartVoice
NextChannel:
inc CurChannel
mov al,CurChannel
cmp al,Channels
jge FinishedLine
add LineOfs,NoteSize ; go to next channel note
jmp ProcessChannel
FinishedLine:
; finished processing current line of the mod
inc CurLine ; finished # of ticks so go to
cmp CurLine,64 ; the next line in the pattern
jl SkipPatternChange
; finished 64 lines so go to the next pattern
cmp JumpToLine,0FFh ; if not 0FFh then a pattern break
je NoPatternBreak ; was specified
mov ax,JumpToLine
mov CurLine,ax
mov JumpToLine,0FFh
jmp PatternBreak
NoPatternBreak:
mov CurLine,0
PatternBreak:
mov ax,NumPats
cmp JumpToPat,0FFh ; if not 0FFh then a position jump
je NoPositionJump ; was specified
mov bx,JumpToPat
mov ScriptPos,bx
mov JumpToPat,0FFh ; reset so pattern jumps don't keep
jmp SongNotOver ; occurring
NoPositionJump:
inc ScriptPos
cmp ScriptPos,ax
jl SongNotOver
mov ax,EndJumpPos
cmp ax,127
jne RestartSong
mov MODPlaying,0
call SetUpMOD
jmp SkipPatternChange
RestartSong:
mov ScriptPos,ax
SongNotOver:
mov bx,ScriptOfs
add bx,ScriptPos
movzx ax,byte ptr es:[di + bx]
mov CurPattern,ax
SkipPatternChange:
NoMODTick:
inc OldCnt
mov ax,OldCnt
cmp ax,OrigRate
jl SkipOrigInt
mov OldCnt,0
pushf
call dword ptr ds:[PreMODInt8]
SkipOrigInt:
mov al,LastVoice ; set the Current Voice back to what
SelectVoice ; it was so other routines aren't
; screwed up
mov al,20h ; reset PIC
out 20h,al
popf ; restore original flags
pop gs
pop di ; restore original registers
pop si
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
iret
MODInt8 ENDP
;══════════════════════════════════════════════════════════════════════
TempBal DB 0
SetUpMOD PROC
mov CurLine,0
mov ScriptPos,0
mov TickCnt,0
mov MODSpeed,6
; get first pattern # from script
les di,[MODData]
mov bx,ScriptOfs
movzx ax,byte ptr es:[di + bx]
mov CurPattern,ax
add bx,128 ; NumPats
mov al,es:[di + bx]
xor ah,ah
mov cs:[NumPats],ax
inc bx
mov al,es:[di + bx]
mov cs:[EndJumpPos],ax
; turn all the channels on
mov ds:[OFFSET ChannelInfo],1
mov ds:[OFFSET ChannelInfo + 83],1
mov ds:[OFFSET ChannelInfo + 166],1
mov ds:[OFFSET ChannelInfo + 249],1
mov TempBal,2
les di,[MODData] ; segment:offset of MOD data
mov al,es:[di + 2011] ; number of channels
mov MaxTrax,al
mov bx,OFFSET ChannelInfo
mov cl,0
SetUpVoiceLoop:
mov al,cl
SelectVoice
push cx
push bx
mov cx,0FFF0h
mov bx,0FFF0h
call RampVolume
xor bx,bx
call RampRate
mov bl,3
call VolControl
mov bx,0140h
call MODSetVol
movzx bx,TempBal
call VoiceBalance
cmp TempBal,13
jne SetBalRight
mov TempBal,2
jmp SetBalLeft
SetBalRight:
mov TempBal,13
SetBalLeft:
pop bx
mov byte ptr ds:[bx],01h ; channel on
mov byte ptr ds:[bx + 1],00h; channel volume = 0
mov byte ptr ds:[bx + 2],00h; channel hit = 0
add bx,ChannelInfoSize
pop cx
inc cl
cmp cl,MaxTrax
jl SetUpVoiceLoop
ret
SetUpMOD ENDP
;══════════════════════════════════════════════════════════════════════
GUS_StartMOD PROC FAR
push bp
mov bp,sp
call SetUpMOD
mov MODPlaying,1
mov sp,bp
pop bp
ret
GUS_StartMOD ENDP
;══════════════════════════════════════════════════════════════════════
GUS_ContinueMOD PROC FAR
push bp
mov bp,sp
mov MODPlaying,1
mov sp,bp
pop bp
ret
GUS_ContinueMOD ENDP
;══════════════════════════════════════════════════════════════════════
GUS_StopMOD PROC FAR
push bp
mov bp,sp
mov MODPlaying,0
les di,[MODData] ; segment:offset of MOD data
mov al,es:[di + 2011] ; number of channels
mov MaxTrax,al
mov al,0
StopVoices:
SelectVoice
call StopVoice
inc al
cmp al,MaxTrax
jne StopVoices
mov sp,bp
pop bp
ret
GUS_StopMOD ENDP
;══════════════════════════════════════════════════════════════════════
CODE ENDS
END