;*	GUS.ASM
;*
;* Gravis Ultrasound Sound Device
;*
;* $Id: gus.asm,v 1.2 1996/08/04 11:27:42 pekangas Exp $
;*
;* Copyright 1996 Petteri Kangaslampi and Jarno Paananen
;*
;* This file is part of the MIDAS Sound System, and may only be
;* used, modified and distributed under the terms of the MIDAS
;* Sound System license, LICENSE.TXT. By continuing to use,
;* modify or distribute this file you indicate that you have
;* read the license and understand and accept it fully.
;*

comment #

Preliminary InterWave support added on 08-Mar-1996
by Jarno Petteri Heikkinen (japehe@cc.jyu.fi)

historical notes:

13/04/96      "upgraded" gus code from pre-release 4

10/03/96      made somewhat "midas compliant" by defining two different
	      cards (gusCardTypes)
	      support for InterWave's >4MB memory does not exist
	      (cant test it anyway, I have only 2MB)

08/03/96      I dont have a clue about how MIDAS Surround thing works
	      so it prolly doesnt :)

#

IDEAL
P386
JUMPS

INCLUDE "lang.inc"
INCLUDE "errors.inc"
INCLUDE "sdevice.inc"
INCLUDE "mmem.inc"
INCLUDE "mglobals.inc"
INCLUDE "mutils.inc"

; This switch determines whether GUS SoundDevice should use full volume
; ramps when setting instruments (no clicks, but slower) or just faster
; ramping.  Value 1 sets fast ramps.

FASTGUS = 0

;/***************************************************************************\
;*	 enum gusFunctIDs
;*	 ----------------
;* Description:  ID numbers for GUS Sound Device functions
;\***************************************************************************/

enum	gusFunctIDs \
ID_gusDetect = ID_gus, \
ID_gusInit, \
ID_gusClose, \
ID_gusGetMixRate, \
ID_gusGetMode, \
ID_gusOpenChans, \
ID_gusCloseChans, \
ID_gusClearChans, \
ID_gusMute, \
ID_gusPause, \
ID_gusSetMasterVolume, \
ID_gusGetMasterVolume, \
ID_gusSetAmplification, \
ID_gusGetAmplification, \
ID_gusPlaySound, \
ID_gusReleaseSound, \
ID_gusStopSound, \
ID_gusSetRate, \
ID_gusGetRate, \
ID_gusSetVolume, \
ID_gusGetVolume, \
ID_gusSetSample, \
ID_gusGetSample, \
ID_gusSetPosition, \
ID_gusGetPosition, \
ID_gusGetDirection, \
ID_gusSetPanning, \
ID_gusGetPanning, \
ID_gusMuteChannel, \
ID_gusAddSample, \
ID_gusRemoveSample, \
ID_gusSetUpdRate, \
ID_gusStartPlay, \
ID_gusPlay, \
ID_gusInitHeap, \
ID_gusFreeHeap, \
ID_gusMalloc, \
ID_gusFree, \
ID_gusAllocBlock, \
ID_gusCoreFree


;/***************************************************************************\
;*	ENUM gusCardTypes
;*	----------------
;* Description: Sound Card type number for GUS Sound Device
;\***************************************************************************/

enum	gusCardTypes \
	gusAutoType = 0, \		; autodetect card type
	gusGF1, \			; original GF1 based card
	gusIW				; AMD InterWave based card


DATASEG

IFDEF	__PASCAL__
	EXTRN	GUS:SoundDevice
ENDIF

STRUC	gusInstrument
	D_int	sampleType	; sampleType
	D_long	sample		; ptr to sample in GUS mem
	D_long	surround	; ptr to surround sample in GUS mem / 0
	D_int	length		; length in bytes
	D_int	loopMode	; See enum
	D_int	loopNum 	; In loop 1/2 (0 if not looped [anymore])
	D_int	loop1Type	; See enum
	D_int	loop1Start	; Offset from beg.
	D_int	loop1End	; Offset from beg.
	D_int	loop2Type	; See enum
	D_int	loop2Start	; Offset from beg.
	D_int	loop2End	; Offset from beg.
ENDS



; STATUS BITS:

enum	gusStatusBits \
	gsStop = 1, \		; Stop voice
	gsRetrig = 2, \ 	; Retrig note
	gsVolume = 4, \ 	; Set volume
	gsFC = 8, \		; Set FC
	gsSample = 16, \	; Sample changed
	gsOnOff = 32, \ 	; Sample on/off (used for pause)
	gsPanning = 64, \	; Set panning
	gsMute = 128, \ 	; Muted
	gsReleased = 256	; Note released



STRUC	gusChannel
	D_int	status		; See enum above
	D_int	inst		; Number
	D_int	curinst 	; Current instrument playing
	D_int	fc		; In FC-format
	D_long	frequency	; In Hz
	D_int	volume		; 0-64
	D_int	surround	; Surround flag
	D_int	sampleType	; See enum
	D_int	loopMode	; See enum
	D_int	loop1Type	; See enum
	D_int	loop2Type	; See enum
	D_long	scurrent	; Current position for GUS
	D_long	sstart		; Sample start for GUS
	D_long	send		; Sample end for GUS
	D_long	l2start 	; Loop 2 start for GUS
	D_long	l2end		; Loop 2 end for GUS
	D_int	panning 	; Panning position (see enum)
	D_int	panningHW	; Panning hardware value
	D_int	onoff		; On / Off switch (DEBUG!)
ENDS


STRUC	ghb			; GUS Heap Block
	D_long	next		; Pointer to next block
	D_long	gusmem		; Pointer to GUS memory
	D_long	length		; Length of this block (Rounded to 32 byte border)
ENDS

; lengthFlags:
;		0 free / allocated



D_int	chancount	; Amount of channels
D_int	voicesel	; Voice Select register
D_int	selreg		; Select Register

D_int	mixfreq 	; Mixing frequency
D_int	updRate 	; SD update rate

D_int	instpos 	; Instrument to be filled next

D_int	muted		; 0 = unmuted, 1 = muted
D_int	paused		; 0 = not, 1 = paused
D_int	mastervol	; Default = max
D_int	masterchanged	; Overrides channel set volume
D_int	numInsts	; Max instrument.

D_long	memamount	; Amount of memory on GUS
D_long	memavail	; Memory available on GUS
D_long	largestblock	; Largest block of memory on GUS
D_long	gusHeapStart	; First block of GUS heap
D_long	gusHeap 	; Pointer to GUS heap
D_int	monoFlag	; Force mono output
D_long	temp		; Temporary storage
D_int	flag		; Internal flag
D_int	chansOpen	; channels open flag
D_int	gusAmp		; amplification value

label channels gusChannel
	rept	32
	gusChannel	?
	endm

D_long	Instruments	; Pointer to GUS instruments



IDATASEG


; If compiling for Pascal, Sound Device name is gusSD, from which the data
; will be copied to Sound Device GUS, defined in Pascal.

IFDEF	__PASCAL__
SDNAM		equ	gusSD
ELSE
SDNAM		equ	GUS
ENDIF


GLOBAL	SDNAM : SoundDevice

; sdMono or sdStereo or sd16bit, \

SDNAM	SoundDevice	< \
1, \					; Called according to tempo
sdUsePort or sdUseOutputMode, \
220h, 0, 0, \				; Port, IRQ, DMA
gusAutoType, 2,\			; Card, num.cards
sdMono or sdStereo or sd16bit, \
ptr_to gusSDName, \
ptr_to gusCardNames, \
6, ptr_to gusPortAddresses, \
ptr_to gusDetect, \
ptr_to gusInit, \
ptr_to gusClose, \
ptr_to gusGetMixRate, \
ptr_to gusGetMode, \
ptr_to gusOpenChans, \
ptr_to gusCloseChans, \
ptr_to gusClearChans, \
ptr_to gusMute, \
ptr_to gusPause, \
ptr_to gusSetMasterVolume, \
ptr_to gusGetMasterVolume, \
ptr_to gusSetAmplification, \
ptr_to gusGetAmplification, \
ptr_to gusPlaySound, \
ptr_to gusReleaseSound, \
ptr_to gusStopSound, \
ptr_to gusSetRate, \
ptr_to gusGetRate, \
ptr_to gusSetVolume, \
ptr_to gusGetVolume, \
ptr_to gusSetSample, \
ptr_to gusGetSample, \
ptr_to gusSetPosition, \
ptr_to gusGetPosition, \
ptr_to gusGetDirection, \
ptr_to gusSetPanning, \
ptr_to gusGetPanning, \
ptr_to gusMuteChannel, \
ptr_to gusAddSample, \
ptr_to gusRemoveSample, \
ptr_to gusSetUpdRate, \
ptr_to gusStartPlay, \
ptr_to gusPlay >

gusSDName	DB	"Gravis Ultrasound Sound Device v4.10",0

gusCardNames	DD ptr_to gusName
		DD ptr_to iwName

;gusName         DB "Gravis GF1 (Compatible mode)", 0
;iwName          DB "AMD InterWave (Enhanced mode)", 0
gusName         DB      "Gravis UltraSound (GF1 Hardware mixing)",0
iwName          DB      "GUS PnP (AMD Interwave Hardware mixing)",0

IFDEF __16__
gusPortAddresses DW	210h, 220h, 230h, 240h, 250h, 260h
ELSE
gusPortAddresses DD	210h, 220h, 230h, 240h, 250h, 260h
ENDIF

gusIRQAddresses  db 2, 3, 5, 7, 11, 12, 15
gusIRQints	 db 71h, 0bh, 0dh, 0fh, 73h, 74h, 77h


; gusInterrupt	  db 0		  ; fuck

env		DB "ULTRASND",0
env_iw		DB "INTERWAVE", 0

; Mixing frequencies for channel amounts 14-32

chantab dw	44100,41160,38587,36317,34300,32494,30870,29400,28063,26843
	dw	25725,24696,23746,22866,22050,21289,20580,19916,19293



LABEL	voltable	WORD
	dw	01500h,08f10h,09f10h,0ab50h,0af10h,0b970h,0bb50h,0bd30h
	dw	0bf10h,0c880h,0c970h,0ca60h,0cb50h,0cc40h,0cd30h,0ce20h
	dw	0cf10h,0d800h,0d880h,0d8f0h,0d970h,0d9e0h,0da60h,0dad0h
	dw	0db50h,0dbc0h,0dc40h,0dcb0h,0dd30h,0dda0h,0de20h,0de90h
	dw	0df10h,0df80h,0e800h,0e840h,0e880h,0e8b0h,0e8f0h,0e930h
	dw	0e970h,0e9a0h,0e9e0h,0ea20h,0ea60h,0ea90h,0ead0h,0eb10h
	dw	0eb50h,0eb80h,0ebc0h,0ec00h,0ec40h,0ec70h,0ecb0h,0ecf0h
	dw	0ed30h,0ed60h,0eda0h,0ede0h,0ee20h,0ee50h,0ee90h,0eed0h
	dw	0ef00h

	; Louder volume table

	IF	0
	dw	01500h,09c10h,0ac10h,0b910h,0bc10h,0bf10h,0c910h,0ca90h
	dw	0cc10h,0cd90h,0cf10h,0d850h,0d910h,0d9d0h,0da90h,0db50h
	dw	0dc10h,0dcd0h,0dd90h,0de50h,0df10h,0dfd0h,0e850h,0e8b0h
	dw	0e910h,0e970h,0e9d0h,0ea30h,0ea90h,0eaf0h,0eb50h,0ebb0h
	dw	0ec10h,0ec70h,0ecd0h,0ed30h,0ed90h,0edf0h,0ee50h,0eeb0h
	dw	0ef10h,0ef70h,0efd0h,0f820h,0f850h,0f880h,0f8b0h,0f8e0h
	dw	0f910h,0f940h,0f970h,0f9a0h,0f9d0h,0fa00h,0fa30h,0fa60h
	dw	0fa90h,0fac0h,0faf0h,0fb20h,0fb50h,0fb80h,0fbb0h,0fbe0h
	dw	0fc00h
	ENDIF

; Panning table for InterWave (pan registers are logarithmic too !!!)
label pantable word
	dw 0FFF0h, 06FD0h, 05FD0h, 05670h, 04FD0h, 04AB0h, 04670h, 042E0h
	dw 03FD0h, 03D20h, 03AB0h, 03870h, 03670h, 034A0h, 032E0h, 03150h
	dw 02FD0h, 02E70h, 02D20h, 02BE0h, 02AB0h, 02990h, 02870h, 02770h
	dw 02670h, 02580h, 024A0h, 023C0h, 022E0h, 02210h, 02150h, 02090h
	dw 01FD0h, 01F20h, 01E70h, 01DC0h, 01D20h, 01C70h, 01BE0h, 01B40h
	dw 01AB0h, 01A20h, 01990h, 01900h, 01870h, 017F0h, 01770h, 016F0h
	dw 01670h, 01600h, 01580h, 01510h, 014A0h, 01430h, 013C0h, 01350h
	dw 012E0h, 01280h, 01210h, 011B0h, 01150h, 010F0h, 01090h, 01030h
label pantablemid word
	dw 00FD0h, 00F70h, 00F20h, 00EC0h, 00E70h, 00E10h, 00DC0h, 00D70h
	dw 00D20h, 00CD0h, 00C70h, 00C30h, 00BE0h, 00B90h, 00B40h, 00AF0h
	dw 00AB0h, 00A60h, 00A20h, 009D0h, 00990h, 00940h, 00900h, 008C0h
	dw 00870h, 00830h, 007F0h, 007B0h, 00770h, 00730h, 006F0h, 006B0h
	dw 00670h, 00640h, 00600h, 005C0h, 00580h, 00550h, 00510h, 004D0h
	dw 004A0h, 00460h, 00430h, 003F0h, 003C0h, 00380h, 00350h, 00320h
	dw 002E0h, 002B0h, 00280h, 00250h, 00210h, 001E0h, 001B0h, 00180h
	dw 00150h, 00120h, 000F0h, 000C0h, 00090h, 00060h, 00030h, 00000h,0



CODESEG

;******* GUS Register Select - Macro ************

MACRO	regsel	register
	mov	_dx,[selreg]
	mov	al,register
	out	dx,al
ENDM

;******* GUS-delay subroutine ************

PROC	gusdelay NEAR
	push	dx ax
	mov	dx,300h
	rept	8
	in	al,dx
	endm
	pop	ax dx
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusDetect(int *result)
;*
;* Description: Detects Gravis UltraSound and fills in GUS SoundDevice
;*		structure with appropriate values for port, DMA and IRQ
;*
;* Returns:	MIDAS error code.
;*		1 stored to *result if GUS was detected, 0 if not.
;*
;\***************************************************************************/

PROC	gusDetect _funct   result : _ptr
USES    _bx

; interwave detect
	cmp	[GUS.cardType],gusAutoType	; has a card type been set?
	jne	@@cardtypeset			; if not, detect it

; this finds out if there's INTERWAVE env str set and forces
; native interwave mode if it exists... dunno about better way to detect ;)

IFDEF	__16__
	call	mGetEnv LANG, seg env offset env_iw
	mov	bx,ax
	or	bx,dx
ELSE
	call	mGetEnv LANG, ptr_to env_iw
	test	_ax, _ax
ENDIF
	mov	_dx, gusGF1
	jz	@@plaingus
	mov	_dx, gusIW
@@plaingus:
	mov	[GUS.cardType], _dx
@@cardtypeset:

IFDEF	__16__
	call	mGetEnv LANG, seg env offset env
	mov	bx,ax
	or	bx,dx
	jz	@@noGUS
	mov	bx,ax
	mov	es,dx			; point es:bx to environment string
ELSE
	call	mGetEnv LANG, ptr_to env
	test	eax,eax
	jz	@@noGUS
	mov	ebx,eax
ENDIF
	mov	cl,3				; 3 digits
	xor	_ax,_ax
@@dloop:
	mov	dl,[_esbx]
	inc	_bx
	sub	dl,"0"
	shl	_ax,4
	add	al,dl
	dec	cl
	jnz	@@dloop
	mov	[GUS.port],_ax
;
;	 mov	 cl, 3			 ; now skip three commas
;@@comma:
;	 mov	 al, [_esbx]
;	 inc	 _bx
;	 test	 al, al 		 ; if zero the ULTRASND env is wrong
;	 jz	 @@noGUS
;	 cmp	 al, ','
;	 jnz	 @@comma
;	 dec	 cl
;	 jnz	 @@comma
;
;	 mov	 al,[_esbx]		 ; first irq digit (skip comma)
;	 sub	 al,"0"
;	 mov	 ah, 10
;	 cmp	 [byte _esbx+1], ','
;	 jz	 @@onedigitirq
;	 imul	 ah
;	 add	 al,[_esbx+1]		 ; second irq digit is necessary
;	 sub	 al,"0"
;@@onedigitirq:
;	 xor	 ah, ah
;	 xor	 _bx, _bx
;@@scanirqlist:
;	 cmp	 [gusIRQAddresses+_bx], al
;	 jz	 @@scanirqfound
;	 inc	 _bx
;	 cmp	 _bx, 7
;	 jb	 @@scanirqlist
;	 jmp	 @@noGUS
;@@scanirqfound:
;	 cmp	 al, 2
;	 jnz	 @@not2nd
;	 mov	 al, 9
;@@not2nd:
;	 mov	 [GUS.IRQ],_ax
;
;	 mov	 al, [gusIRQints+_bx]	 ; translate irq to interrupt number
;	 mov	 [gusInterrupt], al
;	 inc	 bl			 ; irq ports start from 0 unlike our irq list
;	 mov	 _dx, [GUS.port]
;	 add	 dl, 0bh		 ; irq control register
;	 mov	 al, bl
;	 out	 dx, al
;

; Test if there really is a GUS

	add	_ax,103h
	mov	[selreg],_ax			; Register select (2x0h+103h)

	call	gusReset

	regsel	44h
	add	_dx,2
	xor	_ax,_ax
	out	dx,al				; upper bits of address

	regsel	43h
	inc	_dx
	xor	_ax,_ax 			; Address 0
	out	dx,ax
	add	_dx,3
	mov	al,055h
	out	dx,al				; Poke data 1 (55h)

	sub	_dx,3
	mov	_ax,1				; Address 1
	out	dx,ax
	add	_dx,3
	mov	al,0AAh
	out	dx,al				; Poke data 2 (AAh)

	sub	_dx,3
	xor	_ax,_ax 			; Address 0
	out	dx,ax
	add	_dx,3
	in	al,dx				; Peek data 1
	cmp	al,055h
	jne	@@noGUS

	sub	_dx,3
	mov	_ax,1				; Address 1
	out	dx,ax
	add	_dx,3
	in	al,dx				; Peek data 2
	cmp	al,0AAh
	je	@@found

@@noGUS:
	mov	[GUS.port],0
	xor	_ax,_ax 			; No GUS found
	jmp	@@quit

@@found:
	mov	_ax,1				; GUS found
@@quit:
	LOADPTR es,_bx,[result]
	mov	[_esbx],_ax
	xor	_ax,_ax
	ret
ENDP

;/***************************************************************************\
;*
;* Function:	int gusInit(int rate, int mode)
;*
;* Description: Initializes the GUS for playing
;*
;* Input:	int rate		Mixing rate (no effect on GUS)
;*		int mode		Mode (see enum) (mono flag only)
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusInit _funct	   rate : _int, mode : _int
USES    _di,_bx

	mov	[instpos],1			; First instrument to be filled
	mov	[numInsts],0			; No instruments
	mov	[mastervol],64			; Default master volume
	mov	[masterchanged],0
	mov	[flag],0
	mov	[chansOpen],0

	mov	_ax,[mode]
	and	_ax,sdMono			; AND mono bit
	mov	[monoFlag],_ax			; 1 if mono, 0 otherwise

;	 mov	 [monoFlag],0

	mov	_ax,[GUS.port]			; Base address (Set before!)
	add	_ax,103h
	mov	[selreg],_ax			; Register select (2x0h+103h)
	dec	_ax
	mov	[voicesel],_ax			; Voice select (2x0h+102h)

	call	gusReset			; Reset GUS

	cmp	[GUS.cardType], gusIW
	jnz	@@noiwfound			; interwave init?

; interwave to enhanced mode (19h bit 0)
	regsel	19h
	add	_dx, 2
	mov	al, 01h 			; set enhanced mode
	out	dx, al
@@noiwfound:

	mov	[memamount],0			; Initial memory amount
	mov	_cx,64				; Max amount of 256k chunks(IW)
	xor	_bx,_bx 			; Start from 00000h

@@memloop:
	regsel	44h
	add	_dx,2
	mov	_ax,_bx
	out	dx,al				; upper bits of address

	regsel	43h
	inc	_dx
	xor	_ax,_ax 			; Address 0
	out	dx,ax
	add	_dx,3
	mov	al,055h
	out	dx,al				; Poke data 1 (55h)

	sub	_dx,3
	mov	_ax,1				; Address 1
	out	dx,ax
	add	_dx,3
	mov	al,0AAh
	out	dx,al				; Poke data 2 (AAh)

	sub	dx,3
	xor	_ax,_ax 			; Address 0
	out	dx,ax
	add	_dx,3
	in	al,dx				; Peek data 1
	cmp	al,055h
	jne	@@poiss

	sub	_dx,3
	mov	_ax,1				; Address 1
	out	dx,ax
	add	_dx,3
	in	al,dx				; Peek data 2
	cmp	al,0AAh
	jne	@@poiss

	add	[memamount],256*1024		; Add amount of memory by 256k
	add	_bx,4				; Next 256k chunk
	dec	_cx
	jnz	@@memloop

@@poiss:					; No more memory
	cmp	[memamount],0
	jne	@@memok 			; NO MEMORY!

	mov	_ax,errSDFailure		; NO GUS
	jmp	@@err

@@memok:
	mov	eax,[memamount] 		; Initialize mem variables
	mov	[memavail],eax
	mov	[largestblock],eax


; Clear the all 32 channels

	mov	_dx,[GUS.port]			; Mixer
	mov	al,3
	out	dx,al				; Disable Line in & out

	regsel	0eh
	add	_dx,2
	mov	al,31 OR 0c0h
	out	dx,al				; Set number of active
						; voices to 32 just for
						; sure

	mov	_cx,32				; Number of voices
@@resetloop:
	mov	_dx,[voicesel]			; Voice Select
	mov	_ax,_cx
	out	dx,al

	regsel	0				; Voice control
	add	_dx,2				; data low
	mov	al,3				; Stop voice
	out	dx,al

	regsel	9				; Current Volume
	inc	_dx				; data high
	mov	_ax,0500h			; Zero volume
	out	dx,ax

	regsel	12				; Pan Position
	add	_dx,2				; data low
	mov	al,8				; Center
	out	dx,al

	regsel	13				; Volume Ramping
	add	_dx,2				; data low
	mov	al,3				; disable
	out	dx,al

	regsel	6				; Ramp Rate
	add	_dx,2
	mov	al,3fh				; Rate
	out	dx,al

	cmp	[GUS.cardType], gusIW
	jnz	@@noiwregs

	regsel	15h				;interwave synth mode select
	add	_dx, 2
	mov	al, 22h ; deactivate voice (bit 1), enhanced panning (bit 5)
	out	dx, al

	regsel	10h				;interwave synth upper addr
	add	_dx, 2
	xor	eax, eax
	out	dx, al

	regsel	13h				;interwave synth left offset
	inc	dl
	mov	bx, [pantablemid]
	mov	ax, bx
	out	dx, ax
	regsel	1ch
	inc	dl
	mov	ax, bx
	out	dx, ax

	regsel	0ch				;interwave synth right offset
	inc	dl
	mov	ax, bx
	out	dx, ax
	regsel	1bh
	inc	dl
	mov	ax, bx
	out	dx, ax
@@noiwregs:
	dec	_cx
	jnz	@@resetloop

	regsel	4ch				; RESET
	add	_dx,2
	mov	al,3
	out	dx,al				; Enable GF1 and DACs

	mov	_dx,[GUS.port]
	xor	al,al
	out	dx,al				; Enable line in and out

	call	initHeap			; Initialize GUS-heap
	test	_ax,_ax
	jne	@@err

IFDEF	__16__
	call	memAlloc LANG, MAXSAMPLES * SIZE gusInstrument,\
		seg temp offset temp		; Alloc room for instruments
ELSE
	call	memAlloc LANG, MAXSAMPLES * SIZE gusInstrument,\
		ptr_to temp			; Alloc room for instruments
ENDIF
	test	_ax,_ax
	jne	@@err

	COPYPTR [Instruments],[temp]

	LOADPTR es,_di,[Instruments]
IFDEF	__32__
	push	ds
	pop	es
ENDIF
	xor	_ax,_ax
	mov	_cx,MAXSAMPLES * SIZE gusInstrument
	cld
	rep	stosb				; Clear instrument datas
	xor	_ax,_ax
	ret

@@err:	ERROR	ID_gusInit			; Heap error
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	gusReset
;*
;* Description: Resets the GUS card
;*
;* Destroys:	al, dx
;*
;* FOR GUS SOUNDDEVICE INTERNAL USE ONLY!
;*
;\***************************************************************************/

PROC	gusReset NEAR
	regsel	4ch				; RESET
	add	_dx,2
	xor	al,al
	out	dx,al				; RESET!

	call	gusdelay
	call	gusdelay

	mov	al,1
	out	dx,al				; Enable GF1

	call	gusdelay
	call	gusdelay
	ret
ENDP

;/***************************************************************************\
;*
;* Function:	int gusClose()
;*
;* Description: Closes up the GUS.
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusClose _funct
USES    _bx

	call	gusReset

	regsel	19h				; interwave to gus mode
	add	_dx, 2
	xor	al, al				; set gus compatible mode
	out	dx, al

	mov	_dx,[GUS.port]			; Mixer
	xor	al,al
	out	dx,al				; Enable Line in & out

	call	freeHeap
	test	_ax,_ax
	jnz	@@err

	call	memFree LANG, [Instruments]	; Free instruments
	test	_ax,_ax
	jnz	@@err

	mov	[instpos],1			; Flush instruments
	ret					; ax already 0

@@err:	ERROR	ID_gusClose
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusGetMixRate(int *rate)
;*
;* Description: Returns the mixing rate of the SD
;*
;* Returns:	MIDAS error code.
;*		Mixing rate is written to *rate.
;*
;\***************************************************************************/

PROC	gusGetMixRate _funct	   rate : _ptr
USES    _bx

	mov	_ax,[mixfreq]			 ; Get mixing rate
	LOADPTR es,_bx,[rate]
	mov	[_esbx],_ax
	xor	_ax,_ax 			 ; Can't fail
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusGetMode(int *mode)
;*
;* Description: Returns the output mode
;*
;* Returns:	MIDAS error code.
;*		Output mode is written to *mode.
;*
;\***************************************************************************/

PROC	gusGetMode	_funct	   mode : _ptr
USES    _bx

	mov	_ax,sd16bit
	mov	_bx,2
	sub	_bx,[monoFlag]			; 1 if mono (=sdMono)
						; 2 if not (=sdStereo)
	or	_ax,_bx
	LOADPTR es,_bx,[mode]
	mov	[_esbx],_ax
	xor	_ax,_ax 			; Can't fail
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusOpenChans(int chans)
;*
;* Description: Opens channels from the GUS
;*
;* Input:	int chans		Number of channels to open
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusOpenChans _funct    chans : _int
USES    _si,_di,_bx

	mov	[gusAmp],64


	mov	_bx,[chans]
	mov	[chancount],_bx

	mov	[muted],0
	mov	[paused],0

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@nsurr
	add	_bx,_bx
	cmp	_bx,32
	ja	@@err
@@nsurr:
ENDIF
	cmp	_bx,14
	jae	@@okei
	mov	_bx,14				; At least 14 channels open
@@okei:
	mov	_cx,_bx

	sub	_bx,14
	add	_bx,_bx
	mov	bx,[chantab+_bx]	       ; Take the mixing rate

	cmp	[GUS.cardType], gusIW
	jnz	@@noiwmixrate
	mov	_bx, 44100		       ; interwave is always 44.1kHz
@@noiwmixrate:
	mov	[mixfreq],_bx

	regsel	0eh
	add	_dx,2
	mov	_ax,_cx
	dec	_ax				; Amount-1
	or	al,0c0h
	out	dx,al

	mov	_ax,ds
	mov	es,_ax
	mov	_di,offset channels
	xor	al,al
	cld
	mov	_cx,size gusChannel*32
	rep	stosb				; Clear channel blocks

	mov	_cx,[chans]			; to the table
	xor	_bx,_bx 			; Start from channel 0
@@panloop:
	mov	_dx,[voicesel]			; Voice Select
	mov	_ax,[chans]
	sub	_ax,_cx
	out	dx,al

	regsel	12				; Pan Position
	add	_dx,2				; data low

	mov	[_bx+channels.panning],panMiddle ; Panning position

	mov	al,8				; Middle
	out	dx,al

	cmp	[GUS.cardType], gusIW
	jnz	@@noiwregs

; interwave synth mode select
	regsel	15h
	add	_dx, 2
	mov	al, 20h 	; activate voice, select enhanced panning
	out	dx, al
; interwave synth right offset ( bigger blocks sound )
	regsel	0ch
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
	regsel	1bh			; right offset fine
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
; interwave synth left offset
	regsel	13h			; left offset
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
	regsel	1ch			; left offset fine
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax

@@noiwregs:

	mov	[_bx+channels.status],gsStop
	cmp	[mEnableSurround],0
	je	@@nss

	mov	_ax,[chancount]
	imul	_ax,_ax,size gusChannel 	; Get surround chan
	mov	_si,_ax
	add	_si,_bx
	mov	[_si+channels.status],gsMute OR gsStop

@@nss:	add	_bx,SIZE gusChannel
	dec	_cx
	jnz	@@panloop

	mov	[chansOpen],1

	mov	_dx,[GUS.port]
	xor	al,al
	out	dx,al				; Enable line in and out

	call	gusMute LANG, 0
	test	_ax,_ax
	jnz	@@err2
	ret

@@err:	mov	_ax,errNoChannels		; Too much channels
@@err2: ERROR	ID_gusOpenChans
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusCloseChans()
;*
;* Description: Closes channels from the GUS
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusCloseChans	 _funct
USES    _bx

	call	gusClearChans LANG
	mov	[chansOpen],0
	mov	[chancount],0
	xor	_ax,_ax 		; Can't fail
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusClearChans()
;*
;* Description: Clears the channels from the GUS
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusClearChans	 _funct
USES    _si,_bx

	xor	_bx,_bx
	mov	_cx,[chancount]
@@clloop:
	mov	_dx,[voicesel]			; Voice Select
	mov	_ax,[chancount]
	sub	_ax,_cx
	out	dx,al

	regsel	0				; Voice control
	add	_dx,2
	mov	al,3
	out	dx,al				; Stop sound

	call	gusdelay

	out	dx,al				; Stop sound

	regsel	12				; Pan Position
	add	_dx,2				; data low

	mov	[_bx+channels.panning],panMiddle ; Panning position
	mov	[_bx+channels.inst],0		; No instrument
	mov	[_bx+channels.curinst],0	; No instrument
	mov	[_bx+channels.status],gsStop

	mov	al,8				; Middle
	out	dx,al

	cmp	[GUS.cardType], gusIW
	jnz	@@noiwregs
; interwave synth mode select
	regsel	0ch
	add	dl, 2
	mov	al, 2			; de-active channel
	out	dx, al
; interwave synth right offset
	regsel	0ch
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
	regsel	1bh			; right offset fine
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
; interwave synth left offset
	regsel	13h			; left offset
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
	regsel	1ch			; left offset fine
	inc	dl
	mov	ax, [pantablemid]
	out	dx, ax
@@noiwregs:


IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@nss

	mov	_ax,[chancount]
	imul	_ax,_ax,size gusChannel 	; Get surround chan
	mov	_si,_ax
	add	_si,_bx
	mov	[_si+channels.status],gsMute OR gsStop

	mov	_dx,[voicesel]			; Voice Select
	mov	_ax,[chancount]
	add	_ax,_ax 			; Surround channel
	sub	_ax,_cx
	out	dx,al

	regsel	0				; Voice control
	add	_dx,2
	mov	al,3
	out	dx,al				; Stop sound

	call	gusdelay

	out	dx,al				; Stop sound

@@nss:
ENDIF
	add	_bx,SIZE gusChannel
	dec	_cx
	jnz	@@clloop
	xor	_ax,_ax 			; Can't fail
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusMute(ushort mute)
;*
;* Description: Mutes/unmutes GUS Sound Device
;*
;* Input:	int mute		UnMute = 0 / Mute = 1
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusMute _funct	   mute : _int
USES    _bx

	mov	_ax,[mute]
	cmp	[muted],_ax
	je	@@done				; Already at that state
	mov	[muted],_ax
	mov	bl,1				; Enable GF1 (Disable DACs)
	test	_ax,_ax
	jnz	@@mute
	mov	bl,3				; Enable DACs (unmute)
@@mute: cli
	regsel	4ch				; RESET
	add	_dx,2
	mov	al,bl
	out	dx,al
	sti
@@done: xor	_ax,_ax 			; Can't fail
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusPause(ushort pause)
;*
;* Description: Pauses/unpauses GUS SD
;*
;* Input:	int pause		Unpause = 0 / Pause = 1
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusPause _funct     pause : _int
USES    _bx

	mov	_ax,[pause]
	cmp	[paused],_ax
	je	@@done				; Already at that state
	mov	[paused],_ax
	test	_ax,_ax
	jnz	@@pause

	xor	_cx,_cx
	xor	_bx,_bx
@@chloop:
	test	[_bx+channels.status],gsOnOff
	jz	@@off

	cli
	mov	_dx,[voicesel]			; Select voice
	mov	_ax,_cx
	out	dx,al

	regsel	0
	add	_dx,2
	mov	_ax,[_bx+channels.loopMode]
	cmp	_ax,sdLoopNone
	je	@@noloop
	cmp	_ax,sdLoopAmigaNone
	je	@@noloop
	cmp	_ax,sdLoopAmiga
	je	@@loopuni

	test	[_bx+channels.status],gsReleased
	jnz	@@loop2

	cmp	[_bx+channels.loop1Type],loopUnidir
	je	@@loopuni
	jmp	@@loopbidi

@@loop2:
	cmp	[_bx+channels.loop2Type],loopNone
	je	@@noloop
	cmp	[_bx+channels.loop2Type],loopBidir
	je	@@loopbidi

@@loopuni:
	mov	al,8
	jmp	@@loopok

@@loopbidi:
	mov	al,24
	jmp	@@loopok

@@noloop:
	xor	al,al

@@loopok:
	out	dx,al
	sti

@@off:	add	_bx,SIZE gusChannel
	inc	_cx
	cmp	_cx,32
	jne	@@chloop
	jmp	@@done

@@pause:
	xor	_cx,_cx
	xor	_bx,_bx
@@chloop2:
	cli
	mov	_dx,[voicesel]			; Select voice
	mov	_ax,_cx
	out	dx,al

	regsel	80h
	add	_dx,2
	in	al,dx
	sti

	and	_ax,1				; Voice on / off
	xor	al,1				; To active high
	shl	al,5				; to bit 5
	and	[_bx+channels.status],NOT gsOnOff
	or	[_bx+channels.status],_ax

	cli
	regsel	0
	add	_dx,2
	mov	al,3
	out	dx,al				; Stop
	sti

	add	_bx,SIZE gusChannel
	inc	_cx
	cmp	_cx,32
	jne	@@chloop2

@@done: xor	_ax,_ax
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusSetMasterVolume(uchar master)
;*
;* Description: Sets the master volume for the GUS
;*
;* Input:	int master		new master volume (0-64)
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetMasterVolume _funct	master : _int
USES    _bx

	mov	_ax,[master]
	cmp	[mastervol],_ax
	je	@@done				; Already at that value
	mov	[mastervol],_ax
	mov	[masterchanged],1
@@done: xor	_ax,_ax 			; Can't fail
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusGetMasterVolume(uchar *masterVolume)
;*
;* Description: Sets the master volume for the GUS
;*
;* Input:	int *masterVolume	pointer to master volume (0-64)
;*
;* Returns:	MIDAS error code. Master volume is written to *masterVolume.
;*
;\***************************************************************************/

PROC	gusGetMasterVolume _funct	masterVolume : _ptr
USES    _bx

	mov	_ax,[mastervol]
	LOADPTR es,_bx,[masterVolume]
	mov	[_esbx],_ax
	xor	_ax,_ax 		; Can't fail
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusSetAmplification(unsigned amplification);
;*
;* Description: Sets amplification level - does nothing except stores the
;*		value for gusGetAmplification().
;*
;* Input:	unsigned amplification	amplification level - 64 = normal
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetAmplification _funct	amplification : _int

	mov	_ax,[amplification]
	mov	[gusAmp],_ax
	xor	_ax,_ax 		; Can't fail
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusGetAmplification(unsigned *amplification);
;*
;* Description: Gets amplification level.
;*
;* Input:	unsigned *amplification   pointer to amplification level
;*
;* Returns:	MIDAS error code. Amplification level is written to
;*		*amplification.
;*
;\***************************************************************************/

PROC	gusGetAmplification _funct	amplification : _ptr
USES    _bx

	mov	_ax,[gusAmp]
	LOADPTR es,_bx,[amplification]
	mov	[_esbx],_ax
	xor	_ax,_ax 		; Can't fail
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusPlaySound(int chan, ulong freq)
;*
;* Description: Starts a sound with a frequency
;*
;* Input:	int chan		Channel number
;*		ulong freq		Playing frequency
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/
PROC	gusPlaySound _funct	chan : _int, freq : dword
USES    _si,_bx
LOCAL	fc : _int

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@errchan

	imul	_bx,_bx,size gusChannel 	; Get channel block

	mov	eax,[freq]
	cmp	[_bx+channels.frequency],eax
	je	@@nochange
	mov	[_bx+channels.frequency],eax

	shl	eax,10
	xor	edx,edx
IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,[mixfreq]			; GUS freq = frequency / mixing rate
	idiv	ecx
	and	al,0feh 			; Bit 0 unused
	mov	[_bx+channels.fc],_ax
	mov	[fc],_ax
	or	[_bx+channels.status],gsFC

@@nochange:
	mov	_dx,[_bx+channels.inst] 	; Instrument number
	test	_dx,_dx
	jz	@@errinst			; No instrument?
	cmp	[numInsts],_dx
	jb	@@errinst

	dec	_dx				; Table starts from 1
	imul	_dx,_dx,SIZE gusInstrument

	LOADPTR es,_si,[Instruments]
	add	_si,_dx
	cmp	[_essi+gusInstrument.length],0
	je	@@stop

	mov	ecx,[_essi+gusInstrument.sample] ; Start address
	mov	[_bx+channels.scurrent],ecx	; Tell start position to GUS
	and	[_bx+channels.status],NOT (gsStop OR gsReleased)
	or	[_bx+channels.status],gsRetrig

	mov	[_bx+channels.onoff],1

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel 	  ; Get channel block

	mov	eax,[freq]
	cmp	[_bx+channels.frequency],eax
	je	@@nocha
	mov	[_bx+channels.frequency],eax

	mov	_ax,[fc]
	mov	[_bx+channels.fc],_ax
	or	[_bx+channels.status],gsFC

@@nocha:
	mov	ecx,[_essi+gusInstrument.surround] ; Start address
	test	ecx,ecx
	jnz	@@ok2
	mov	ecx,[_essi+gusInstrument.sample] ; Start address
@@ok2:	mov	[_bx+channels.scurrent],ecx	; Tell start position to GUS
	and	[_bx+channels.status],NOT (gsStop OR gsReleased)
	or	[_bx+channels.status],gsRetrig
	mov	[_bx+channels.onoff],1
ENDIF
@@quit:
	xor	_ax,_ax
	ret

@@stop: call	gusStopSound LANG, [chan]
	ret

@@errchan:
	mov	_ax,errInvalidChanNumber
	jmp	@@err
@@errinst:
	mov	_ax,errInvalidSampleHandle
@@err:	ERROR	ID_gusPlaySound
	ret

ENDP


;/***************************************************************************\
;*
;* Function:	int gusReleaseSound(int chan)
;*
;* Description: Releases a sound in channel
;*
;* Input:	int chan		Channel number
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusReleaseSound _funct	   chan : _int
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@errchan

	imul	_bx,_bx,size gusChannel 	; Get channel block
	or	[_bx+channels.status],gsReleased
	xor	_ax,_ax
	ret

@@errchan:
	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusReleaseSound
	ret


ENDP

;/***************************************************************************\
;*
;* Function:	int gusStopSound(int chan)
;*
;* Description: Stops sound on a channel
;*
;* Input:	int chan		Channel number
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusStopSound _funct	chan : _int
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	imul	_bx,_bx,size gusChannel 	; Channel block

	and	[_bx+channels.status],NOT gsRetrig
	or	[_bx+channels.status],gsStop

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel 	; Channel block

	and	[_bx+channels.status],NOT gsRetrig
	or	[_bx+channels.status],gsStop
ENDIF
@@quit: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusStopSound
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusSetRate(int chan, ulong freq)
;*
;* Description: Sets the playing rate for a channel
;*
;* Input:	int chan		Channel number
;*		ulong freq		New playing frequency
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetRate _funct     chan : _int, freq : dword
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	imul	_bx,_bx,size gusChannel

	mov	eax,[freq]
	cmp	[_bx+channels.frequency],eax
	je	@@quit
	mov	[_bx+channels.frequency],eax

	shl	eax,10
	xor	edx,edx
IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,[mixfreq]			; GUS freq = frequency / mixing rate
	idiv	ecx
	and	al,0feh 			; bit 0 unused
	mov	[_bx+channels.fc],_ax
	or	[_bx+channels.status],gsFC

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel

	mov	[_bx+channels.fc],_ax
	mov	eax,[freq]
	mov	[_bx+channels.frequency],eax
	or	[_bx+channels.status],gsFC
ENDIF
@@quit: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusSetRate
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusGetRate(int chan, ulong *freq)
;*
;* Description: Returns the playing rate for a channel
;*
;* Input:	int chan		Channel number
;*		ulong *freq		Pointer to frequency
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusGetRate	_funct	   chan : _int, freq : _ptr
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	cli
	mov	_dx,[voicesel]			 ; Select voice
	mov	_ax,_bx
	out	dx,al

	regsel	80h				; Voice control
	add	_dx,2
	in	al,dx
	sti

	test	al,1
	jnz	@@stopped			; Is sound on?

	mov	_bx,[chan]
	imul	_bx,_bx,size gusChannel

	mov	eax,[_bx+channels.frequency]
	LOADPTR es,_bx,[freq]
	mov	[_esbx],eax
	xor	_ax,_ax
	ret

@@stopped:
	LOADPTR es,_bx,[freq]
	mov	[dword _esbx],0
	xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetRate
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusSetVolume(int chan, int volume)
;*
;* Description: Sets the volume for a channel
;*
;* Input:	int chan		Channel number
;*		int volume		New playing volume
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/
PROC	gusSetVolume	_funct	   chan : _int, vol : _int
USES    _bx

	mov	_ax,[vol]
	cmp	_ax,64
	jbe	@@not64 			; Max volume = 64
	mov	_ax,64
@@not64:
	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	imul	_bx,_bx,size gusChannel

	cmp	[_bx+channels.volume],_ax
	je	@@quit

	mov	[_bx+channels.volume],_ax
	or	[_bx+channels.status],gsVolume

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel

	mov	[_bx+channels.volume],_ax
	or	[_bx+channels.status],gsVolume
ENDIF
@@quit: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusSetVolume
	ret

ENDP


;/***************************************************************************\
;*
;* Function:	int gusGetVolume(int chan, int *volume)
;*
;* Description: Sets the volume for a channel
;*
;* Input:	int chan		Channel number
;*		int *volume		Pointer to volume
;*
;* Returns:	MIDAS error code. Playing volume is written to *volume
;*
;\***************************************************************************/

PROC	gusGetVolume	_funct	   chan : _int, volume : _ptr
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	imul	_bx,_bx,size gusChannel

	mov	_ax,[_bx+channels.volume]
	LOADPTR es,_bx,[volume]
	mov	[_esbx],_ax

	xor	_ax,_ax
	jmp	@@done

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetVolume

@@done:
	ret

ENDP




;/***************************************************************************\
;*
;* Function:	int gusSetSample(int chan, int sample)
;*
;* Description: Sets up a sample for playing
;*
;* Input:	int chan		Channel number
;*		int inst		Sample number from AddSample
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetSample	  _funct     chan : _int, inst : _int
USES    edi,_si,_bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@errchn
	imul	_bx,_bx,size gusChannel 	; Channel block

	mov	_ax,[inst]
	test	_ax,_ax
	jz	@@errinst			; No instrument at all?
	cmp	[numInsts],_ax
	jb	@@errinst

	mov	_dx,_ax
	dec	_dx				; Table starts from 1
	imul	_dx,_dx,SIZE gusInstrument
	LOADPTR es,_si,[Instruments]
	add	_si,_dx

	cmp	[_bx+channels.inst],_ax
	je	@@nochange

	mov	[_bx+channels.inst],_ax 	; Set instrument

	cmp	[_essi+gusInstrument.length],0
	je	@@stop

	mov	_cx,[_essi+gusInstrument.sampleType]
	mov	[_bx+channels.sampleType],_cx

	mov	ecx,[_essi+gusInstrument.sample] ; Start address
	mov	[_bx+channels.scurrent],ecx
	mov	edi,ecx

	mov	_dx,[_essi+gusInstrument.loop1Type]
	mov	[_bx+channels.loop1Type],_dx	; Copy loop 1 type
	mov	_dx,[_essi+gusInstrument.loop2Type]
	mov	[_bx+channels.loop2Type],_dx	; Copy loop 2 type

	mov	_dx,[_essi+gusInstrument.loopMode]
	mov	[_bx+channels.loopMode],_dx	; Copy loop mode

	cmp	_dx,sdLoopNone
	je	@@noloop
	cmp	_dx,sdLoopAmigaNone
	je	@@noloop

IFDEF	__16__
	xor	eax,eax
ENDIF
	push	ecx edi
	mov	_ax,[_essi+gusInstrument.loop2End]
	add	ecx,eax 			; Loop 2 end address
	mov	[_bx+channels.l2end],ecx
	mov	_ax,[_essi+gusInstrument.loop2Start]
	add	edi,eax 			; Loop 2 end address
	mov	[_bx+channels.l2start],edi
	pop	edi ecx

	mov	_ax,[_essi+gusInstrument.loop1End]
	add	edi,eax 			; Loop end address
	mov	_ax,[_essi+gusInstrument.loop1Start]
	add	ecx,eax 			; Loop start address
	jmp	@@duu

@@noloop:
IFDEF	__16__
	xor	eax,eax
ENDIF
	mov	_ax,[_essi+gusInstrument.length]
	add	edi,eax 			; Sample end address
@@duu:
	mov	[_bx+channels.sstart],ecx	; Tell loop start to GUS
	mov	[_bx+channels.send],edi 	; And loop end
	or	[_bx+channels.status],gsSample

@@nochange:
IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel 	; Channel block

	mov	_ax,[inst]

	cmp	[_bx+channels.inst],_ax
	je	@@nochange2

	mov	[_bx+channels.inst],_ax 	; Set instrument

	mov	ecx,[_essi+gusInstrument.surround] ; Start address
	test	ecx,ecx
	jnz	@@ok2
	mov	ecx,[_essi+gusInstrument.sample] ; Start address
@@ok2:
	mov	edi,ecx
IFDEF	__16__
	xor	eax,eax
ENDIF
	mov	_dx,[_essi+gusInstrument.loopMode]
	mov	[_bx+channels.loopMode],_dx	; Copy loop mode

	cmp	_dx,sdLoopNone
	je	@@noloop2
	cmp	_dx,sdLoopAmigaNone
	je	@@noloop2

	mov	_ax,[_essi+gusInstrument.loop1End]
	add	edi,eax 			; Loop end address
	mov	_ax,[_essi+gusInstrument.loop1Start]
	add	ecx,eax 			; Loop start address
	jmp	@@duu2

@@noloop2:
	mov	_ax,[_essi+gusInstrument.length]
	add	edi,eax 			; Sample end address
@@duu2:
	mov	[_bx+channels.sstart],ecx	; Tell loop start to GUS
	mov	[_bx+channels.send],edi 	; And loop end
	or	[_bx+channels.status],gsSample

@@nochange2:
ENDIF
@@quit:
	xor	_ax,_ax
	ret

@@stop: call	gusStopSound LANG, [chan]
	ret

@@errinst:
	mov	_ax,errInvalidSampleHandle
	jmp	@@err
@@errchn:
	mov	_ax,errInvalidChanNumber
@@err:	ERROR	ID_gusSetSample
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusGetSample(int chan, ushort *sample)
;*
;* Description: Gets the last sample set for playing
;*
;* Input:	int chan		channel number
;*		int *sample		pointer to sample number
;*
;* Returns:	MIDAS error code. Sample number is written to *sample.
;*
;\***************************************************************************/

PROC	gusGetSample	_funct	   chan : _int, inst : _ptr
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@errchn
	imul	_bx,_bx,size gusChannel 	  ; Channel block

	mov	_ax,[_bx+channels.inst]
	LOADPTR es,_bx,[inst]
	mov	[_esbx],_ax

	xor	_ax,_ax
	jmp	@@done

@@errchn:
	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetSample

@@done:
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusSetPosition(int chan, ushort pos)
;*
;* Description: Sets the playing position for a channel
;*
;* Input:	int chan		Channel number
;*		int pos 		New playing position
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/
PROC	gusSetPosition _funct	  chan : _int, pos : _int
USES    _si,_bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err
	imul	_bx,_bx,size gusChannel 	; Channel block

	mov	_dx,[_bx+channels.inst]
	test	_dx,_dx
	jz	@@quit				; No instrument?

	dec	_dx				; Table starts from 1
	imul	_dx,_dx,SIZE gusInstrument
	LOADPTR es,_si,[Instruments]
	add	_si,_dx

IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,[pos]
	cmp	[_essi+gusInstrument.sampleType],smp16bit
	jne	@@skip
	add	_cx,_cx
@@skip: cmp	[_essi+gusInstrument.length],_cx ; Over from end?
	jae	@@ok

	cmp	[_essi+gusInstrument.loopMode],sdLoopAmiga
	je	@@ale

	call	gusStopSound LANG, [chan]
	ret

@@ale:	mov	_cx,[_essi+gusInstrument.loop1Start] ; Yep. Start from loop

@@ok:	add	ecx,[_essi+gusInstrument.sample] ; Add sample start to position
	mov	[_bx+channels.scurrent],ecx	 ; Set start position
	and	[_bx+channels.status],NOT gsStop
	or	[_bx+channels.status],gsRetrig
	mov	[_bx+channels.onoff],1

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@quit

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,size gusChannel 	  ; Channel block
IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,[pos]
	cmp	[_essi+gusInstrument.surround],0
	je	@@nop
	add	ecx,[_essi+gusInstrument.surround] ; Add sample start to position
	jmp	@@kop
@@nop:	add	ecx,[_essi+gusInstrument.surround] ; Add sample start to position
@@kop:	mov	[_bx+channels.scurrent],ecx	 ; Set start position
	and	[_bx+channels.status],NOT gsStop
	or	[_bx+channels.status],gsRetrig
	mov	[_bx+channels.onoff],1
ENDIF
@@quit: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusSetPosition
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusGetPosition(int chan, ushort *pos)
;*
;* Description: Gets the playing position of a channel
;*
;* Input:	int chan	     Channel number
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusGetPosition _funct	  chan : _int, pos:_ptr
USES    _si,_bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err

	cli
	mov	_dx,[voicesel]			; Select voice
	mov	_ax,_bx
	out	dx,al

	regsel	80h				; Voice control
	add	_dx,2
	in	al,dx
	test	al,1
	jnz	@@stopped			; Is sound on?

	imul	_bx,_bx,size gusChannel 	; Channel block

	;FIXME!
	cmp	[_bx+channels.sampleType],smp16bit
	je	@@stopped

@@huuko:
	regsel	8ah				; Current position high
	inc	_dx
	in	ax,dx
IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,_ax
	and	_cx,01fffh
	shl	ecx,7

	regsel	8bh				; Current position low
	inc	_dx
	in	ax,dx
	shr	ax,9
	or	_cx,_ax

	call	gusdelay

; Read the value again to avoid GF1 self-modifying...
	regsel	8ah				; Current position high
	inc	_dx
	in	ax,dx
IFDEF	__16__
	xor	esi,esi
ENDIF
	mov	_si,_ax
	and	_si,01fffh
	shl	esi,7

	regsel	8bh				; Current position low
	inc	dx
	in	ax,dx
	shr	ax,9
	or	_si,_ax

; Some sanity checking...
	sub	esi,ecx
	js	@@huuko
	cmp	esi,16
	ja	@@huuko

	mov	_bx,[_bx+channels.curinst]
	dec	_bx
	imul	_bx,_bx,SIZE gusInstrument

	LOADPTR es,_si,[Instruments]
	sub	ecx,[_essi+_bx+gusInstrument.sample]
	jns	@@oke2

	xor	ecx,ecx
	jmp	@@oke
@@oke2:
	cmp	ecx,[_essi+_bx+gusInstrument.length]
	jb	@@oke

	mov	ecx,[_essi+_bx+gusInstrument.length]
	dec	ecx

@@oke:	sti
	LOADPTR es,_bx,[pos]
	mov	[_esbx],_cx
	xor	_ax,_ax
	ret

@@stopped:					; No sound is being played
	xor	_cx,_cx
	jmp	@@oke
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetPosition
	ret

ENDP


;/***************************************************************************\
;*
;* Function:	int gusGetDirection(int chan, ushort *dir)
;*
;* Description: Gets the playing direction of a channel
;*
;* Input:	int chan		Channel number
;*		int dir 		Pointer to direction
;*
;* Returns:	MIDAS error code.
;*		Playing direction is stored in *dir.
;*
;\***************************************************************************/

PROC	gusGetDirection _funct	   chan : _int, dir : _ptr
USES    _bx

	mov	_ax,[chan]
	cmp	[chancount],_ax
	jle	@@err

	cli
	mov	_dx,[voicesel]			; Select voice
	out	dx,al

	regsel	80h				; Voice control
	add	_dx,2
	in	al,dx
	sti

	test	al,40h
	jnz	@@back
	mov	_ax,1
	jmp	@@end
@@back:
	mov	_ax,-1
@@end:	LOADPTR es,_bx,[pos]
	mov	[_esbx],_ax
	xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetDirection
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusSetPanning(int chan, int panning)
;*
;* Description: Sets the panning position for a channel
;*
;* Input:	int channel		Channel number
;*		int panning		Panning value (See enum)
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetPanning	_funct	   chan : _int, panning : _int
USES    _bx

	mov	_ax,[chan]
	cmp	[chancount],_ax
	jle	@@err
	mov	_dx,[voicesel]			; Voice Select
	out	dx,al

	mov	_bx,_ax
	imul	_bx,_bx,size gusChannel 	; Channel block

	mov	_ax,[panning]
	cmp	[_bx+channels.panning],_ax
	je	@@kwit
	mov	[_bx+channels.panning],_ax	; Panning position

IFNDEF NOSTEREO
	cmp	[monoFlag],1
	je	@@kwit				; Skip if forced mono

	cmp	_ax,panSurround
	jne	@@juuh

	cmp	[mEnableSurround],0
	jne	@@huu
	xor	_ax,_ax 			; No surround enabled --> middle
	jmp	@@juuh

@@huu:
	mov	[_bx+channels.panningHW],0	; Extreme left
	or	[_bx+channels.status],gsPanning

	mov	[_bx+channels.surround],1	; This is a surround channel
	mov	_ax,[_bx+channels.status]	; Copy status (mute)

	mov	_cx,[chancount]
	imul	_cx,_cx,size gusChannel 	; Channel block
	add	_bx,_cx

	mov	[_bx+channels.panningHW],15	; Extreme right
	mov	[_bx+channels.surround],1	; This is a surround, too
	mov	[_bx+channels.status],_ax
	jmp	@@jatkuu

@@juuh: mov	[_bx+channels.surround],0	; Normal channel

	sar	_ax,3
	or	_ax,_ax 			; Sign
	jns	@@pos
	inc	_ax
@@pos:	add	_ax,7
	mov	[_bx+channels.panningHW],_ax
	or	[_bx+channels.status],gsPanning

	mov	_bx,[chan]
	add	_bx,[chancount]
	cmp	_bx,32
	jae	@@jatkuu
	imul	_bx,_bx,size gusChannel 	; Channel block
	mov	[_bx+channels.surround],0	; No longer surround channel (?)
	or	[_bx+channels.status],gsMute

@@jatkuu:
	mov	[masterchanged],1		; Force reset of volume
ENDIF
@@kwit: sti
@@quit: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusSetPanning
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusGetPanning(int chan, short *panning)
;*
;* Description: Gets the panning position for a channel
;*
;* Input:	int channel		Channel number
;*		int *panning		Pointer to panning value
;*
;* Returns:	MIDAS error code.
;*		Panning value is stored in *panning.
;*
;\***************************************************************************/

PROC	gusGetPanning	_funct	   channel : _int, panning : _ptr
USES    _bx

	mov	_bx,[channel]
	cmp	[chancount],_bx
	jle	@@err
	imul	_bx,_bx,size gusChannel 	; Channel block
	mov	_ax,[_bx+channels.panning]	; Panning position
	LOADPTR es,_bx,[panning]
	mov	[_esbx],_ax
	xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusGetPanning
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusMuteChannel(int chan, int mute)
;*
;* Description: Mutes or unmutes a channel
;*
;* Input:	int chan		Channel number
;*		int mute		Unmute = 0 / Mute = 1
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusMuteChannel	_funct	   chan : _int, mute : _int
USES    _bx

	mov	_bx,[chan]
	cmp	[chancount],_bx
	jle	@@err
	imul	_bx,_bx,SIZE gusChannel

	cmp	[mute],1
	je	@@mute
	and	[_bx+channels.status],NOT gsMute
	or	[_bx+channels.status],gsVolume
	jmp	@@guu
@@mute:
	or     [_bx+channels.status],gsMute OR gsVolume

@@guu:
IFNDEF NOSTEREO
	cmp	[_bx+channels.surround],0
	je	@@pois

	mov	_bx,[chan]
	add	_bx,[chancount]
	imul	_bx,_bx,SIZE gusChannel

	cmp	[mute],1
	je	@@mute2
	and	[_bx+channels.status],NOT gsMute
	or	[_bx+channels.status],gsVolume
	jmp	@@pois
@@mute2:
	or     [_bx+channels.status],gsMute OR gsVolume
ENDIF
@@pois: xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidChanNumber
	ERROR	ID_gusMuteChannel
	ret

ENDP


;/***************************************************************************\
;*
;* Function:	int gusAddSample(sdSample *sample, int copySample,
;*				 int *sdNum);
;*
;* Description:  Adds a new sample to the sample list
;*
;* Input:	 sdSample *sample	pointer to sample information
;*					structure.
;*		 int copySample 	1 if sample should be copied
;*		 int *sdNum		pointer to variable to store the SD
;*					sample handle
;*
;* Returns:	MIDAS error code.
;*		SD sample handle is stored in *sdNum.
;*
;\***************************************************************************/

PROC	gusAddSample _funct	sample : _ptr, copysample : _int, sdNum : _ptr
USES    _si,_di,_bx
LOCAL	gusmem : dword, gusmem2 : dword, smpStart : _ptr, glength : _int, \
	loopMode : _int, loopStart : _int

	LOADPTR gs,_si,[sample]
	cmp	[_gssi+sdSample.sampleType],smp8bit
	je	@@instok
	cmp	[_gssi+sdSample.sampleType],smp16bit
	je	@@instok
	cmp	[_gssi+sdSample.sampleType],smpNone
	jne	@@errinst
	mov	[_gssi+sdSample.sampleLength],0
@@instok:
	mov	_ax,[instpos]
	dec	_ax				; Table starts from 1
	imul	_ax,_ax,SIZE gusInstrument
	LOADPTR es,_di,[Instruments]
	add	_di,_ax

	cmp	[_gssi+sdSample.sampleLength],0
	je	@@nsurr

	cmp	[_gssi+sdSample.sampleType],smp8bit
	jne	@@not8bit

	; FIXME!!!!
	mov	_cx,[_gssi+sdSample.sampleLength]

	PUSHSEGREG es
	LOADPTR es,_bx,[_gssi+sdSample.sample]	; Pointer to sample
@@signloop:
	xor	[byte _esbx],80h
	inc	_bx
	dec	_cx
	jnz	@@signloop
	POPSEGREG es

@@not8bit:
	PUSHSEGREG es gs
	call	gusMalloc LANG, [_gssi+sdSample.sampleLength],	\
		 [_gssi+sdSample.sampleType], ptr_to temp
	POPSEGREG gs es
	test	_ax,_ax
	jnz	@@err

	mov	eax,[temp]
	mov	[gusmem],eax

	mov	[_esdi+gusInstrument.sample],eax   ; Sample start
	mov	[_esdi+gusInstrument.surround],0

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@nsurr
	cmp	[_gssi+sdSample.sampleType],smp16bit
	je	@@nsurr

	PUSHSEGREG es
	call	gusMalloc LANG, [_gssi+sdSample.sampleLength],	\
		 [_gssi+sdSample.sampleType], ptr_to temp
	POPSEGREG es
	test	_ax,_ax
	jnz	@@err

	mov	eax,[temp]
	mov	[gusmem2],eax
	mov	[_esdi+gusInstrument.surround],eax ; Surround sample start
ENDIF
@@nsurr:
	COPYPTR [smpStart],[_gssi+sdSample.sample]

	; FIXME!
	mov	_ax,[_gssi+sdSample.sampleType]
	cmp	_ax,smpNone
	jne	@@smp2
	mov	_ax,smp8bit
@@smp2:
	mov	[_esdi+gusInstrument.sampleType],_ax

	mov	_ax,[_gssi+sdSample.sampleLength]
	mov	[_esdi+gusInstrument.length],_ax ; Sample length

	mov	_ax,[_gssi+sdSample.loopMode]
	mov	[_esdi+gusInstrument.loopMode],_ax
	mov	[loopMode],_ax

	mov	_ax,[_gssi+sdSample.loop1Type]
	mov	[_esdi+gusInstrument.loop1Type],_ax

	mov	_ax,[_gssi+sdSample.loop1Start]
	mov	[_esdi+gusInstrument.loop1Start],_ax

	mov	_ax,[_gssi+sdSample.loop1End]
	mov	[_esdi+gusInstrument.loop1End],_ax

	mov	_ax,[_gssi+sdSample.loop2Type]
	mov	[_esdi+gusInstrument.loop2Type],_ax

	mov	_ax,[_gssi+sdSample.loop2Start]
	mov	[_esdi+gusInstrument.loop2Start],_ax

	mov	_ax,[_gssi+sdSample.loop2End]
	mov	[_esdi+gusInstrument.loop2End],_ax

;PK-
	cmp	[loopMode],sdLoop2
	jne	@@loop1
;-PK
	cmp	[_gssi+sdSample.loop2Type],loopNone
	je	@@loop1
	mov	_ax,[_gssi+sdSample.loop2Start]
	jmp	@@loopok
@@loop1:
	mov	_ax,[_gssi+sdSample.loop1Start]
@@loopok:
	mov	[loopStart],_ax

	mov	_cx,[_gssi+sdSample.sampleLength]
	test	_cx,_cx
	jz	@@qwit

	LOADPTR es,_si,[smpStart]		; Pointer to sample

	mov	ebx,[gusmem]			; Start in GUS memory
IFDEF	__32__
	xor	edi,edi
ENDIF
	mov	di,bx
	shr	ebx,16

	mov	[flag],1

@@dumploop2:
	cli
	regsel	44h				; Addr hi
	add	_dx,2				; 3x5
	mov	_ax,_bx
	out	dx,al				; upper bits of address
	sti

	regsel	43h				; Addr lo
@@dumploop:
	mov	_dx,[selreg]
	mov	_ax,_di
	inc	_dx				; 3x4
	out	dx,ax
	add	_dx,3				; 3x7
	mov	al,[_essi]			; Get data
	inc	_si
	out	dx,al

	inc	di
	jz	@@yli64
	dec	_cx
	jnz	@@dumploop			; Dump the whole sample
	jmp	@@quit

@@yli64:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop2			; Dump the whole sample


@@quit:
	cmp	[loopMode],sdLoopNone
	je	@@noloob
	cmp	[loopMode],sdLoop1Rel
	je	@@noloob
	cmp	[loopMode],sdLoopAmigaNone
	je	@@noloob
@@loobed:					; Copy loop to next 32 byte border
	LOADPTR es,_si,[smpStart]
	add	_si,[loopStart]

	mov	_dx,_di
	mov	_cx,64
	and	_dx,01fh			 ; AND lowest bits off
	sub	_cx,_dx

@@dumploop3:
	cli
	regsel	44h				; Addr hi
	add	_dx,2				; 3x5
	mov	_ax,_bx
	out	dx,al				; upper bits of address
	sti

	regsel	43h				; Addr lo
@@dumploop4:
	mov	_dx,[selreg]
	mov	_ax,_di
	inc	_dx				; 3x4

	out	dx,ax
	add	_dx,3				; 3x7
	mov	al,[_essi]			; Get data
	inc	_si
	out	dx,al

	inc	di
	jz	@@yli642
	dec	_cx
	jnz	@@dumploop4			; Do whole loop
	jmp	@@qwti

@@yli642:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop3			; Do whole loop
	jmp	@@qwti


@@noloob:					; Copy the last byte
	dec	_si
	mov	_dx,_di
	mov	_cx,64
	and	_dx,01fh			; AND lowest bits away...
	sub	_cx,_dx

@@dumploop5:
	cli
	regsel	44h				; addr hi
	add	_dx,2				; 3x5
	mov	_ax,_bx
	out	dx,al				; upper bits of address
	sti

	regsel	43h				; addr lo
@@dumploop6:
	mov	_dx,[selreg]
	inc	_dx				; 3x4
	mov	_ax,_di

	out	dx,ax
	mov	al,[_essi]			; Get data
	add	_dx,3				; 3x7
	out	dx,al

	inc	di
	jz	@@yli643
	dec	_cx
	jnz	@@dumploop6
	jmp	@@qwti

@@yli643:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop5

@@qwti:
	LOADPTR gs,_si,[sample]

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@qwit
	cmp	[_gssi+sdSample.sampleType],smp16bit
	je	@@qwit

	call	CopySurroundSample LANG, [smpStart], [glength], [loopStart],\
					 [loopMode], [gusmem2]

ENDIF
@@qwit:
	cmp	[_gssi+sdSample.sampleType],smp8bit
	jne	@@not8bit2

	; FIXME!!!!
	mov	_cx,[_gssi+sdSample.sampleLength]
	test	_cx,_cx
	jz	@@not8bit2

	PUSHSEGREG es
	LOADPTR es,_bx,[_gssi+sdSample.sample]	; Pointer to sample
@@signloop2:
	xor	[byte _esbx],80h
	inc	_bx
	dec	_cx
	jnz	@@signloop2
	POPSEGREG es

@@not8bit2:
	push	[instpos]			; Return instrument number

	mov	[flag],0

	mov	_ax,[instpos]
	mov	_bx,_ax
	dec	_bx
	imul	_bx,_bx,SIZE gusInstrument
	LOADPTR es,_si,[Instruments]
	add	_si,_bx
@@search:					; Search next free instrument
	cmp	[_essi+gusInstrument.sampleType],smpNone
	je	@@found
	add	_si,SIZE gusInstrument
	inc	_ax
	jmp	@@search

@@found:
	mov	[instpos],_ax
	pop	_ax
	LOADPTR es,_bx,[sdNum]
	mov	[_esbx],_ax

	cmp	[numInsts],_ax
	jae	@@huu
	mov	[numInsts],_ax
@@huu:
	xor	_ax,_ax
	ret


@@errinst:
	mov	_ax,errInvalidInst
@@err:	ERROR	ID_gusAddSample
	ret
ENDP


IFNDEF NOSTEREO
PROC	CopySurroundSample NEAR inst : _ptr, glength : _int, loopStart : _int,\
				gloop : _int, gusmem : dword
USES    _si,_di,_bx

	LOADPTR es,_si,[inst]			; Pointer to sample

	mov	_cx,[glength]

	mov	ebx,[gusmem]			; Start in GUS memory
	mov	_di,_bx
	shr	ebx,16

@@dumploop2:
	cli
	regsel	44h
	add	_dx,2				;3x5
	mov	_ax,_bx
	out	dx,al				;upper bits of address
	sti

	regsel	43h
@@dumploop:
	mov	_dx,[selreg]
	inc	_dx				; 3x4
	mov	_ax,_di
	out	dx,ax
	add	_dx,3				; 3x7
	mov	al,[_essi]			; Get data
	inc	_si
	not	al				; 180 degree phase transformation
	out	dx,al

	inc	di
	jz	@@yli64
	dec	_cx
	jnz	@@dumploop			; Dump the whole sample
	jmp	@@quit

@@yli64:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop2			; Dump the whole sample

@@quit:
	cmp	[loopMode],sdLoopNone
	je	@@noloob
	cmp	[loopMode],sdLoop1Rel
	je	@@noloob
	cmp	[loopMode],sdLoopAmigaNone
	je	@@noloob

@@loobed:					; Copy loop to next 32 byte border
	LOADPTR es,_si,[inst]
	add	_si,[loopStart]

	mov	_dx,_di
	and	_dx,01fh			; AND the lowest bits away...
	mov	_cx,64
	sub	_cx,_dx

@@dumploop3:
	cli
	regsel	44h
	add	_dx,2				;3x5
	mov	_ax,_bx
	out	dx,al				;upper bits of address
	sti

	regsel	43h
@@dumploop4:
	mov	_dx,[selreg]
	mov	_ax,_di
	inc	_dx				; 3x4

	out	dx,ax
	add	_dx,3				; 3x7
	mov	al,[_essi]			; Get data
	inc	_si
	not	al				; 180 degree phase transformation
	out	dx,al

	inc	di
	jz	@@yli642
	dec	_cx
	jnz	@@dumploop4			; Do whole loop
	jmp	@@qwit

@@yli642:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop3			; Do whole loop
	jmp	@@qwit

@@noloob:					; Copy the last byte
	mov	_dx,_di
	dec	_si
	and	_dx,01fh			; Alimmat pois
	mov	_cx,64
	sub	_cx,_dx

@@dumploop5:
	cli
	regsel	44h
	add	_dx,2				;3x5
	mov	_ax,_bx
	out	dx,al				;upper bits of address
	sti

	regsel	43h
@@dumploop6:
	mov	_dx,[selreg]
	mov	_ax,_di
	inc	_dx				; 3x4

	out	dx,ax
	add	dx,3				; 3x7
	mov	al,[_essi]			; Get data
	not	al				; 180 degree phase transformation
	out	dx,al

	inc	di
	jz	@@yli643
	dec	_cx
	jnz	@@dumploop6
	jmp	@@qwit
@@yli643:
	inc	_bx				; Crossed 64kb border
	dec	_cx
	jnz	@@dumploop5
@@qwit:
	ret
ENDP
ENDIF

;/***************************************************************************\
;*
;* Function:	int gusRemoveSample(int sample)
;*
;* Description: Removes a sample from the GUS SD internal tables
;*		and frees it from GUS memory
;*
;* Input:	int sample		Sample number from AddSample
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusRemoveSample _funct	   sample : _int
USES    _si,_bx

	mov	_bx,[inst]
	dec	_bx
	imul	_bx,_bx,SIZE gusInstrument
	LOADPTR es,_si,[Instruments]
	add	_si,_bx

	cmp	[_essi+gusInstrument.sampleType],smpNone
	je	@@nothing

	mov	[_essi+gusInstrument.sampleType],smpNone ; Free instrument

	cmp	[_essi+gusInstrument.length],0
	je	@@nsurround

	PUSHSEGREG es
	call	gusFree LANG, [_essi+gusInstrument.sample]
	POPSEGREG es
	test	_ax,_ax
	jnz	@@err

IFNDEF NOSTEREO
	cmp	[_essi+gusInstrument.surround],0
	je	@@nsurround

	call	gusFree LANG, [_essi+gusInstrument.surround]
	test	_ax,_ax
	jnz	@@err
ENDIF
@@nsurround:
	mov	_ax,[inst]
	cmp	[instpos],_ax
	jbe	@@nothing

	mov	[instpos],_ax			; Lowest instrument number
@@nothing:
	cmp	[numInsts],_ax
	jne	@@juu

	LOADPTR es,_si,[Instruments]
	mov	_cx,_ax
	mov	_bx,1
	mov	_ax,_bx
@@search:					; Search next free instrument
	cmp	[_essi+gusInstrument.sampleType],smpNone
	je	@@nop
	mov	_ax,_bx
@@nop:	add	_si,SIZE gusInstrument
	inc	_bx
	dec	_cx
	jnz	@@search
	mov	[numInsts],_ax
@@juu:	xor	_ax,_ax
	ret

@@err:	ERROR	ID_gusRemoveSample
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusSetUpdRate(int rate)
;*
;* Description: Sets the update rate of SD.
;*
;* Input:	int rate		Rate in Hz*100
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/

PROC	gusSetUpdRate _funct	 rate : _int

	mov	_ax,[rate]
	mov	[updRate],_ax
	xor	_ax,_ax
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	 int gusStartPlay(void);
;*
;* Description:  Called before gusPlay() is called. This function doesn't
;*		 have to do anything, however.
;*
;* Returns:	 MIDAS error code
;*
;\***************************************************************************/

PROC	gusStartPlay	_funct

	xor	_ax,_ax
	ret
ENDP




;/***************************************************************************\
;*
;* Function:	int gusPlay(int *callMP)
;*
;* Description: Updates the GUS registers according to the Sound Device
;*		internal datas
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/
PROC	gusPlay _funct	   callMP : _ptr
LOCAL	chanc : _int, selsave : _int, voicesave : _int, retrigfl : _int
USES    _di,_si,_bx

	cmp	[paused],0
	jne	@@paused

	cmp	[chansOpen],0
	je	@@paused

	xor	_ax,_ax
	mov	_dx,[voicesel]
	in	al,dx
	mov	[voicesave],_ax

	mov	_dx,[selreg]
	in	al,dx
	cmp	[flag],0			; Are we uploading
	je	@@kd				; at the same time?
	mov	al,43h
@@kd:	mov	[selsave],_ax

	mov	[chanc],0			; Start from channel 0
	xor	_si,_si 			; Channel data
@@loop:
	cmp	[_si+channels.onoff],0
	je	@@retrigged

	mov	_dx,[voicesel]			; Select voice
	mov	_ax,[chanc]
	out	dx,al

	test	[_si+channels.status],gsStop OR gsRetrig ; Retrig / stop sound?
	jz	@@nothing

	and	[_si+channels.status],NOT gsStop

IF FASTGUS
	test	[_si+channels.status],gsRetrig
	jz	@@juststop

	regsel	9				; Current volume
	inc	_dx
	mov	_ax,1500h
	out	dx,ax

	call	gusdelay

	out	dx,ax
	jmp	@@setfc

@@juststop:
ENDIF
	regsel	7				; Volume ramp start
	add	_dx,2
	mov	al,15h				; to zero
	out	dx,al

	regsel	0dh				; Volume ramp control
	add	_dx,2
	mov	al,40h				; Start decreasing ramp
	out	dx,al

	regsel	0				; Voice control
	add	_dx,2
	mov	al,3				; Stop voice
	out	dx,al

	call	gusdelay

	out	dx,al				; again...

	regsel	0dh				; Volume ramp control
	add	_dx,2
	mov	al,40h				; Start decreasing ramp
	out	dx,al
	jmp	@@setfc

@@nothing:
	cmp	[masterchanged],0
	jnz	@@set
	test	[_si+channels.status],gsVolume
	jz	@@setfc
@@set:	and	[_si+channels.status],NOT gsVolume
	regsel	89h
	inc	_dx
	in	ax,dx

	mov	cx,ax
	xor	cl,cl

	mov	_ax,[_si+channels.volume]	; To set volume
	test	[_si+channels.status],gsMute
	jz	@@oek2
	xor	_ax,_ax
@@oek2: mov	_bx,[mastervol]
	mul	bl
	shr	_ax,6
	mov	_bx,_ax
	add	_bx,_bx
	mov	bx,[voltable+_bx]

	cmp	[_si+channels.surround],0
	je	@@hu2
	sub	bh,10h				; Halve the volume
@@hu2:	xor	bl,bl
	cmp	cx,bx
	je	@@setfc 			; Already at that value
	cmp	cx,bx
	jb	@@yli
	xchg	cx,bx
	mov	cl,40h
@@yli:
	regsel	0dh				; Ramp Control
	add	_dx,2
	mov	al,3
	out	dx,al				; Stop Ramp

	call	gusdelay

	out	dx,al				; Stop Ramp

	regsel	7				; Ramp start
	add	_dx,2
	mov	al,ch				; From lower
	out	dx,al

	regsel	8				; Ramp end
	add	_dx,2
	mov	al,bh				; To higher volume
	out	dx,al

	regsel	0dh				; Ramp Control
	add	_dx,2
	mov	al,cl
	out	dx,al				; GO!

	call	gusdelay

	out	dx,al				; GO!

@@setfc:
	test	[_si+channels.status],gsFC
	jz	@@setpan

	and	[_si+channels.status],NOT gsFC
	regsel	1				; Frequency control
	inc	_dx
@@writefc:
	mov	_ax,[_si+channels.fc]
	out	dx,ax

; Let's see if this helps the Tone Portamento problems... (PK)
; Unfortunately this has no chances of working, reading GUS
; registers is made from register+80h... (JP)
	if	0
	call	gusdelay
	in	ax,dx
	cmp	_ax,[_si+channels.fc]
	je	@@fcok
	call	gusdelay
	jmp	@@writefc
@@fcok:
; End of TP fix
	endif


@@setpan:
	test	[_si+channels.status],gsPanning
	jz	@@stopped

	and	[_si+channels.status],NOT gsPanning

	cmp	[GUS.cardType], gusIW
	jz	@@iwsmoothpan		; no crappy on/off panning for iw ;)
; regular gus compatible panning
	regsel	12				; Pan Position
	add	_dx,2
	mov	_ax,[_si+channels.panningHW]
	out	dx,al
	jmp	@@stopped
@@iwsmoothpan:
	mov	_bx, [_si+channels.panning]
	add	_bx, _bx
;interwave synth right offset ( bigger blocks sound )
	regsel	0ch
	inc	dl
	mov	ax, [pantablemid+_bx]
	out	dx, ax
	regsel	1bh			; right offset fine
	inc	dl
	mov	ax, [pantablemid+_bx]
	out	dx, ax

	neg	_bx
;interwave synth left offset
	regsel	13h			; left offset
	inc	dl
	mov	ax, [pantablemid+_bx]
	out	dx, ax
	regsel	1ch			; left offset fine
	inc	dl
	mov	ax, [pantablemid+_bx]
	out	dx, ax

@@stopped:
	test	[_si+channels.status],gsSample
	jz	@@retrigged			; No sample changed

	cmp	[_si+channels.loopMode],sdLoopAmigaNone
	je	@@ALE
	cmp	[_si+channels.loopMode],sdLoopAmiga
	jne	@@noALE

@@ALE:	test	[_si+channels.status],gsRetrig
	jnz	@@retrigged			; Already to be retrigged

; GUS-AMIGA-LOOP-EMULATOR (GALE) (TM) V1.14!!!

	regsel	80h				; Voice control
	add	_dx,2
	in	al,dx

	test	al,1
	jz	@@soundon

	cmp	[_si+channels.loopMode],sdLoopAmigaNone ; Next sample looped?
	je	@@stopsound			; No

	mov	_ax,[_si+channels.inst]
	mov	[_si+channels.curinst],_ax

	mov	ebx,[_si+channels.sstart]
	jmp	@@startfromloop 		; Start from loop start

@@soundon:
	mov	eax,[_si+channels.frequency]
	imul	eax,eax,100			; updRate is Hz*100
	xor	edx,edx

IFDEF	__16__
	xor	ebx,ebx
ENDIF
	mov	_bx,[updRate]
	idiv	ebx				; eax = bytes to play until next update
	push	eax

	regsel	84h				; Sample end position high
	inc	_dx
	in	ax,dx
IFDEF	__16__
	xor	ebx,ebx
ENDIF
	mov	_bx,_ax
	and	_bx,01fffh
	shl	ebx,7

	regsel	85h				; Sample end position low
	inc	_dx
	in	ax,dx
	shr	ax,9
	or	_bx,_ax

	regsel	8ah				; Current position high
	inc	_dx
	in	ax,dx
IFDEF	__16__
	xor	ecx,ecx
ENDIF
	mov	_cx,_ax
	and	_cx,01fffh
	shl	ecx,7

	regsel	8bh				; Current position low
	inc	_dx
	in	ax,dx
	shr	ax,9
	or	_cx,_ax

	sub	ebx,ecx 			; Bytes to sample / loop end
	pop	eax

	cmp	ebx,eax
	jg	@@retrigged			; Some sample still to go

	test	[_si+channels.loopMode],sdLoopAmigaNone ; Looped?
	je	@@stopsound

	mov	_ax,[_si+channels.inst]
	mov	[_si+channels.curinst],_ax

	cmp	ebx,0
	jg	@@norm

	mov	ebx,[_si+channels.sstart]
	jmp	@@startfromloop

@@norm: mov	ecx,[_si+channels.send]
	sub	ecx,ebx
	mov	ebx,ecx

@@startfromloop:
; EBX = Starting position

	mov	ecx,[_si+channels.send]        ; Set sample end

	regsel	4				; End position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax
	regsel	5				; End position low
	inc	_dx
	mov	eax,ecx
	shl	_ax,9
	out	dx,ax

	mov	ecx,[_si+channels.sstart]      ; Set loop start

	regsel	2				; Start position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax
	regsel	3				; Start position low
	inc	_dx
	mov	eax,ecx
	shl	_ax,9
	out	dx,ax

	regsel	0				; Voice control
	mov	al,8				; Enable voice and loop
	add	_dx,2
	out	dx,al

	regsel	10				; Current position high
	inc	_dx
	mov	eax,ebx
	shr	eax,7
	out	dx,ax
	regsel	11				; Current position low
	inc	_dx
	mov	eax,ebx
	shl	_ax,9
	out	dx,ax

	call	gusdelay			; Delay

	out	dx,ax

	regsel	10				; Current position high
	inc	_dx
	mov	eax,ebx
	shr	eax,7
	out	dx,ax

	regsel	0				; Voice control
	add	_dx,2
	mov	al,8				; Enable voice and loop
	out	dx,al
	jmp	@@retrigged

@@stopsound:
	regsel	0				; Voice control
	add	_dx,2
	mov	al,3				; Stop voice
	out	dx,al

	regsel	9				; Current volume
	inc	_dx
	mov	_ax,1500h			; To zero
	out	dx,ax

	regsel	0dh				; Ramp control
	add	_dx,2
	mov	al,3				; Stop
	out	dx,al

	call	gusdelay

	out	dx,al

	regsel	0				; Voice control
	add	_dx,2
	mov	al,3				; Stop voice
	out	dx,al

	regsel	9				; Current volume
	inc	_dx
	mov	ax,1500h			; To zero
	out	dx,ax

@@noALE:
@@retrigged:
	add	_si,size gusChannel		; Do all channels in order
	inc	[chanc]
	mov	_ax,[chancount]

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@ij
	add	_ax,_ax 			; Plus Surround channels
@@ij:
ENDIF
	cmp	[chanc],_ax
	jb	@@loop

@@waitloop:
	mov	[chanc],0			; Start from channel 0
	xor	_si,_si 			; Channel data
	mov	[retrigfl],0
@@loop2:
	cmp	[_si+channels.onoff],0
	je	@@skip

	test	[_si+channels.status],gsRetrig	; Retrig?
	jz	@@skip

	mov	_dx,[voicesel]			; Select voice
	mov	_ax,[chanc]
	out	dx,al

IFE FASTGUS
	regsel	8dh				; Ramp finished?
	add	_dx,2
	in	al,dx
	test	al,1
	jnz	@@retrig

	mov	[retrigfl],1			; Still voice(s) to wait...
	jmp	@@skip
ENDIF
@@retrig:
	test	[_si+channels.status],gsSample
	jz	@@not				; No sample changed

	mov	ecx,[_si+channels.send] 	; Set sample end
	cmp	[_si+channels.sampleType],smp16bit
	jne	@@spl
; translate 16 bit sample address...
	mov	edx,ecx
	shr	ecx,1
	and	ecx,01ffffh
	and	edx,0c0000h
	xor	ecx,edx
@@spl:
	regsel	4				; End position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax
	regsel	5				; End position low
	inc	_dx
	mov	eax,ecx
	shl	_ax,9
	out	dx,ax

	mov	ecx,[_si+channels.sstart]	; Set loop start
	cmp	[_si+channels.sampleType],smp16bit
	jne	@@spl2
; translate 16 bit sample address...
	mov	edx,ecx
	shr	ecx,1
	and	ecx,01ffffh
	and	edx,0c0000h
	xor	ecx,edx
@@spl2:
	regsel	2				; Start position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax
	regsel	3				; Start position low
	inc	_dx
	mov	eax,ecx
	shl	_ax,9
	out	dx,ax

	and	[_si+channels.status],NOT gsSample

@@not:	regsel	0				; Voice control
	add	_dx,2
	mov	al,3				; Stop voice
	out	dx,al

	mov	ecx,[_si+channels.scurrent]	; Set starting address
	cmp	[_si+channels.sampleType],smp16bit
	jne	@@spl3
; translate 16 bit sample address...
	mov	edx,ecx
	shr	ecx,1
	and	ecx,01ffffh
	and	edx,0c0000h
	xor	ecx,edx
@@spl3:
	regsel	10				; Current position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax
	regsel	11				; Current position low
	inc	_dx
	mov	eax,ecx
	shl	_ax,9
	out	dx,ax

	call	gusdelay

	out	dx,ax

	regsel	10				; Current position high
	inc	_dx
	mov	eax,ecx
	shr	eax,7
	out	dx,ax

	regsel	0				; Voice control
	add	_dx,2
	mov	al,3				; Stop voice
	out	dx,al

	mov	_ax,[_si+channels.inst]
	mov	[_si+channels.curinst],_ax

	regsel	0				; Voice control
	add	dx,2
	mov	_ax,[_si+channels.loop1Type]	; Enable voice and possible loop
	cmp	_ax,loopNone
	je	@@noloop2
	cmp	_ax,loopUnidir
	je	@@loopuni

@@loopbidi:
	mov	al,24
	jmp	@@loopok2
@@loopuni:
	mov	al,8
	jmp	@@loopok2
@@noloop2:
	xor	al,al
@@loopok2:
	mov	_cx,[_si+channels.sampleType]
	cmp	_cx,smp16bit
	jne	@@lok
	or	al,4
@@lok:	out	dx,al

	regsel	8				; Ramp end
	add	_dx,2
	mov	_ax,[_si+channels.volume]	; To set volume

	test	[_si+channels.status],gsMute
	jz	@@oek
	xor	_ax,_ax
@@oek:	mov	_bx,[mastervol]
	mul	bl
	shr	_ax,6
	mov	_bx,_ax
	add	_bx,_bx
	mov	ax,[voltable+_bx]
	mov	al,ah

	cmp	[_si+channels.surround],0
	je	@@hu
	sub	al,10h				; Halve the volume
@@hu:	out	dx,al

	regsel	0dh
	add	_dx,2
	xor	al,al
	out	dx,al

	call	gusdelay			; Delay

	out	dx,al

	regsel	0				; Voice control
	add	dx,2
	mov	_ax,[_si+channels.loop1Type]	; Enable voice and possible loop
	cmp	_ax,loopNone
	je	@@noloop3
	cmp	_ax,loopUnidir
	je	@@loopuni2
@@loopbidi2:
	mov	al,24
	jmp	@@loopok3
@@loopuni2:
	mov	al,8
	jmp	@@loopok3
@@noloop3:
	xor	al,al
@@loopok3:
	cmp	_cx,smp16bit
	jne	@@lok2
	or	al,4
@@lok2: out	dx,al

	and	[_si+channels.status],NOT (gsRetrig OR gsVolume)

@@skip: add	_si,size gusChannel		; Do all channels in order
	inc	[chanc]
	mov	_ax,[chancount]
IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@ij2
	add	_ax,_ax 			; Plus Surround channels
@@ij2:
ENDIF
	cmp	[chanc],_ax
	jb	@@loop2

IFE FASTGUS
	cmp	[retrigfl],0			; Still voices to wait?
	jne	@@waitloop
ENDIF
	mov	_ax,[voicesave]
	mov	_dx,[voicesel]
	out	dx,al

	mov	_ax,[selsave]
	mov	_dx,[selreg]
	out	dx,al

	mov	[masterchanged],0

	LOADPTR es,_bx,[callMP]
	mov	[_int _esbx],1			; Call mp.Play!
@@done: xor	_ax,_ax
	ret

@@paused:
	LOADPTR es,_bx,[callMP]
	mov	[_int _esbx],0			; Don't call mp.Play!
	jmp	@@done
ENDP




;/***************************************************************************\
;*
;* Function:	int initHeap()
;*
;* Description: Initializes the GUS heap
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/


PROC	initHeap NEAR
USES    _di,_bx

	mov	_ax,MAXSAMPLES * SIZE ghb

IFNDEF NOSTEREO
	cmp	[mEnableSurround],0
	je	@@kool

	add	_ax,_ax 			; Room for surround
						; blocks, too
@@kool:
ENDIF
	push	_ax
	call	memAlloc LANG, _ax, ptr_to temp
						; Alloc room for heap blocks
	pop	_cx				; Size to _cx
	test	_ax,_ax
	jne	@@err

	mov	ebx,[temp]
	mov	[gusHeapStart],ebx
	mov	[gusHeap],ebx

	LOADPTR es,_di,[temp]
	xor	al,al
						; Size already in _cx
	cld
	rep	stosb				; Clear instrument datas

	LOADPTR es,_bx,[temp]
	mov	eax,[memamount]
	mov	[_esbx+ghb.next],0		; first and last block
	mov	[_esbx+ghb.gusmem],0		; from the start of mem
	mov	[_esbx+ghb.length],eax		; whole memory
	xor	_ax,_ax
	ret

@@err:	ERROR	ID_gusInitHeap
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int freeHeap()
;*
;* Description: Uninitializes the GUS heap
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/


PROC	freeHeap NEAR
USES    _bx

	call	memFree LANG, [gusHeap]
	test	_ax,_ax
	jnz	@@err
	ret

@@err:	ERROR	ID_gusFreeHeap
	ret
ENDP


;/***************************************************************************\
;*
;* Function:	int gusMalloc(ushort length, ulong *mem)
;*
;* Description: Allocates GUS memory (with best fit alcorithm)
;*
;* Input:	ushort length		Length of the block to be allocated
;*					(Rounded up to next 64 bytes with
;*					at least 32 bytes for loop copy, etc.)
;*
;* Returns:	MIDAS error code.
;*
;\***************************************************************************/
PROC	gusMalloc _funct   glength : _int, gtype : _int, mem : _ptr
USES    _si,_di,_bx
LOCAL	bestfitoff : _int, bestfitseg : _int, leastslack : dword

@@again:
IFDEF	__16__
	xor	eax,eax
ENDIF
	mov	_ax,[glength]
	test	_ax,_ax
	jz	@@done

	mov	ebx,eax 		; Round up to next 64 byte border
	and	ebx,1fh 		; with at least 32 bytes buffer
	mov	ecx,64
	sub	ecx,ebx
	add	eax,ecx

	cmp	eax,256*1024
	jb	@@mok

	cmp	[gtype],smp16bit
	je	@@notenoughmem			; can't play over 256kb
						; 16-; bit samples...
@@mok:	mov	[bestfitseg],0
	mov	[leastslack],7ffffffh

	cmp	[memavail],eax			; Is there that much memory left
	jl	@@notenoughmem

	cmp	[largestblock],eax		; Do we have to defragment?
	jg	@@noneedtodefragment

	push	eax
	call	gusDefragment
	pop	eax

	cmp	[largestblock],eax
	jl	@@notenoughmem

@@noneedtodefragment:
	LOADPTR es,_si,[gusHeapStart]

@@findbest:
	push	eax
	call	findFreeBlock
	pop	eax
	jc	@@notanymore

	mov	ebx,[_essi+ghb.length]
	and	ebx, NOT 1fh			; And flags off
	sub	ebx,eax
	js	@@nxt				; Too small

	cmp	[leastslack],ebx
	jl	@@nxt				; Not the best

	mov	[leastslack],ebx		; Set to be the best
IFDEF	__16__
	mov	_bx,es
	mov	[bestfitseg],_bx
ENDIF
	mov	[bestfitoff],_si

@@nxt:
	mov	ecx,[_essi+ghb.next]		; Advance to next block
	test	ecx,ecx
	jz	@@notanymore
	mov	_si,_cx
IFDEF	__16__
	shr	ecx,16
	mov	es,cx
ENDIF
	jmp	@@findbest

@@notanymore:
IFDEF	__16__
	cmp	[bestfitseg],0	; Was there any free blocks large enough?
	je	@@notenoughmem
ELSE
	cmp	[bestfitoff],0	; Was there any free blocks large enough?
	je	@@notenoughmem
ENDIF
	cmp	[leastslack],0
	je	@@justalloc

@@notfit:
	push	eax
	call	allocBlock LANG, ptr_to temp ; Allocate new block
	test	_ax,_ax
	jnz	@@memerr

	pop	eax
	LOADPTR gs,_di,[temp]

	mov	_si,[bestfitoff]
IFDEF	__16__
	mov	bx,[bestfitseg]
	mov	es,bx
ENDIF
	mov	ebx,[_essi+ghb.gusmem]		; Copy pointer

	cmp	[gtype],smp16bit
	jne	@@kool

	mov	ecx,ebx
	mov	edx,ecx
	add	ecx,eax
	and	ecx,NOT(256*1024-1)
	and	edx,NOT(256*1024-1)
	cmp	ecx,edx
	je	@@kool				; Wholly inside one
						; 256k bank

; We'll have to split this block to two, one for each bank.
@@split:
	mov	ecx,ebx
	add	ecx,256*1024-1
	and	ecx,NOT (256*1024-1)
	mov	[_gsdi+ghb.gusmem],ecx
	sub	ecx,ebx
	mov	ebx,[_essi+ghb.length]
	sub	ebx,ecx
	mov	[_gsdi+ghb.length],ebx		; second block length
	mov	[_essi+ghb.length],ecx		; first block length
	mov	ecx,[_essi+ghb.next]
	mov	ebx,[temp]
	mov	[_gsdi+ghb.next],ecx
	mov	[_essi+ghb.next],ebx

	PUSHSEGREG gs
	call	checkCoreFree			; Update biggest block
	POPSEGREG gs
	test	_ax,_ax
	jnz	@@err
	jmp	@@again

@@kool:
	mov	[_gsdi+ghb.gusmem],ebx
	mov	[_gsdi+ghb.length],eax		; Set block length
	or	[_gsdi+ghb.length],1		; Mark as allocated

	add	[_essi+ghb.gusmem],eax		; Move free block "upwards"
	sub	[_essi+ghb.length],eax		; Sub free block length

IFDEF	__16__
	mov	bx,es
	shl	ebx,16
ENDIF
	mov	_bx,_si

	mov	[_gsdi+ghb.next],ebx		; Link blocks
	cmp	[gusHeapStart],ebx
	jne	@@notfirst			; The first block?

IFDEF	__16__
	mov	bx,gs				; Set this block to HeapStart
	shl	ebx,16
ENDIF
	mov	_bx,_di
	mov	[gusHeapStart],ebx
	jmp	@@donee

@@notfirst:
	push	eax
	mov	eax,ebx
	call	findPrevBlock			; Find block linked to the free block
	pop	eax
	jc	@@heapcorr			; No such block!

IFDEF	__16__
	mov	bx,gs
	shl	ebx,16
ENDIF
	mov	_bx,_di
	mov	[_essi+ghb.next],ebx		; Link to previous block


@@donee:
	sub	[memavail],eax
	PUSHSEGREG gs
	call	checkCoreFree			; Update biggest block
	POPSEGREG gs
	test	_ax,_ax
	jnz	@@err
	mov	eax,[_gsdi+ghb.gusmem]		; Return pointer
	LOADPTR es,_bx,[mem]
	mov	[_esbx],eax
	xor	_ax,_ax
	ret

@@justalloc:					; Realloc?
	mov	ebx,[_essi+ghb.gusmem]		; Copy pointer

	cmp	[gtype],smp16bit
	jne	@@kool2

	mov	ecx,ebx
	mov	edx,ecx
	add	ecx,eax
	and	ecx,NOT(256*1024-1)
	and	edx,NOT(256*1024-1)
	cmp	ecx,edx
	jne	@@notfit			; we'll have to split...
@@kool2:
	or	[_essi+ghb.length],1
	sub	[memavail],eax
	PUSHSEGREG es
	call	checkCoreFree
	POPSEGREG es
	test	_ax,_ax
	jnz	@@err
	mov	eax,[_essi+ghb.gusmem]
@@done: LOADPTR es,_bx,[mem]
	mov	[_esbx],eax
	xor	_ax,_ax
	ret

@@memerr:
	pop	ebx				; pop saved pointer
	jmp	@@err

@@heapcorr:
	mov	_ax,errCardHeapCorrupted
	jmp	@@err

@@notenoughmem:
	mov	_ax,errOutOfCardMemory
@@err:	ERROR	ID_gusMalloc
	ret
ENDP



;/***************************************************************************\
;*
;* Function:	int gusFree(ulong mem)
;*
;* Description: Deallocates GUS memory
;*
;* Input:	ulong block		Pointer to allocated GUS mem
;*
;* Returns:	1 if success, 0 if not
;*
;* Destroys:	ax, bx, cx, dx
;*
;\***************************************************************************/
PROC	gusFree _funct	 block : dword
USES    _si,_di,_bx
LOCAL	freed : dword

	mov	eax,[block]

	LOADPTR gs,_di,[gusHeapStart]
@@sloop:
	cmp	[_gsdi+ghb.gusmem],eax
	je	@@found

	mov	ebx,[_gsdi+ghb.next]
	test	ebx,ebx
	jz	@@invalid
	mov	_di,_bx
IFDEF	__16__
	shr	ebx,16
	mov	gs,bx
ENDIF
	jmp	@@sloop

@@found:
	test	[_gsdi+ghb.length],1
	jz	@@heapcorr			; Not even allocated

	and	[_gsdi+ghb.length],NOT 1	; Free this block

	mov	ebx,[_gsdi+ghb.length]
	mov	[freed],ebx

	mov	ebx,[_gsdi+ghb.next]
	test	ebx,ebx
	jz	@@nonextblock			; Last block

	mov	_si,_bx
IFDEF	__16__
	shr	ebx,16
	mov	es,bx
ENDIF
	test	[_essi+ghb.length],1
	jnz	@@nonextblock			; Next allocated -> Can't merge blocks

	mov	edx,[_essi+ghb.next]
	mov	[_gsdi+ghb.next],edx
	mov	edx,[_essi+ghb.length]		; Merge blocks
	add	[_gsdi+ghb.length],edx

	PUSHSEGREG gs
IFDEF	__16__
	call	freeBlock LANG, es si		; Free block
ELSE
	call	freeBlock LANG, esi		; Free block
ENDIF
	POPSEGREG gs
	test	_ax,_ax
	jnz	@@err

@@nonextblock:
IFDEF	__16__
	mov	bx,gs
	shl	ebx,16
ENDIF
	mov	_bx,_di
	cmp	[gusHeapStart],ebx
	je	@@firstblock			; First block

	mov	eax,ebx
	call	findPrevBlock
	jc	@@heapcorr			; No such block! (Heap corrupt?)

	test	[_essi+ghb.length],1
	jnz	@@firstblock			; previous allocated -> can't merge blocks

	mov	edx,[_gsdi+ghb.next]
	mov	[_essi+ghb.next],edx
	mov	edx,[_gsdi+ghb.length]		; Merge blocks
	add	[_essi+ghb.length],edx

IFDEF	__16__
	call	freeBlock LANG, gs di		; Free block
ELSE
	call	freeBlock LANG, edi		; Free block
ENDIF
	test	_ax,_ax
	jnz	@@err

@@firstblock:
	mov	eax,[freed]
	add	[memavail],eax
	call	checkCoreFree
	test	_ax,_ax
	jnz	@@err
	xor	_ax,_ax
	ret

@@heapcorr:
	mov	_ax,errCardHeapCorrupted
	jmp	@@err

@@invalid:
	mov	_ax,errInvalidBlock
@@err:	ERROR	ID_gusFree
	ret
ENDP


PROC    allocBlock NEAR block : _ptr
uses    _bx

	LOADPTR es,_bx,[gusHeapStart]
	mov	_cx,MAXSAMPLES
	cmp	[mEnableSurround],0
	je	@@findloop
	add	_cx,_cx 			; Include surround blocks
@@findloop:
	cmp	[_esbx+ghb.length],0
	je	@@found
	add	_bx,size ghb
	dec	_cx
	jnz	@@findloop
	jmp	@@err
@@found:
IFDEF	__16__
	mov	ax,es
	shl	eax,16
ENDIF
	mov	_ax,_bx
	LOADPTR es,_bx,[block]
	mov	[_esbx],eax
	xor	_ax,_ax
	ret

@@err:	mov	_ax,errInvalidBlock
	ERROR	ID_gusAllocBlock
	ret
ENDP

PROC	freeBlock NEAR block : _ptr
USES    _bx

	LOADPTR es,_bx,[block]
	mov	[_esbx+ghb.next],0
	mov	[_esbx+ghb.gusmem],0
	mov	[_esbx+ghb.length],0
	xor	_ax,_ax
	ret
ENDP

; _essi = pointer to current block
; Returns: _essi = next free block
; Carry set if not found

PROC	findFreeBlock NEAR

@@sloop:
	test	[_essi+ghb.length],1
	jz	@@found

	mov	eax,[_essi+ghb.next]   ; Advance to next block
	test	eax,eax
	jz	@@nofree
	mov	_si,_ax
IFDEF	__16__
	shr	eax,16
	mov	es,ax
ENDIF
	jmp	@@sloop

@@found:
	clc
	ret
@@nofree:
	stc
	ret
ENDP


; eax = pointer to current block
; Returns: _essi = prev block
; Carry set if not found

PROC	findPrevBlock NEAR

	LOADPTR es,_si,[gusHeapStart]
@@sloop:
	cmp	[_essi+ghb.next],eax
	je	@@found

	mov	ebx,[_essi+ghb.next]
	test	ebx,ebx
	jz	@@done
	mov	_si,_bx
IFDEF	__16__
	shr	ebx,16
	mov	es,bx
ENDIF
	jmp	@@sloop

@@found:
	clc
	ret
@@done:
	stc
	ret
ENDP



; No parameters, also checks heap integrity
; Returns MIDAS error code

PROC	checkCoreFree	NEAR
USES    _si,_bx

	LOADPTR es,_si,[gusHeapStart]
	xor	edx,edx 			; Start from size 0
	xor	ecx,ecx 			; Total mem size
@@findloop:
	mov	eax,[_essi+ghb.length]
	mov	ebx,eax
	and	ebx,NOT 31			; ebx = size
	add	ecx,ebx 			; Add to total
	test	eax,1				; Allocated flag
	jnz	@@findnext			; Allocated
	cmp	ebx,edx 			; Largest?
	jle	@@findnext
	mov	edx,ebx
@@findnext:
	mov	eax,[_essi+ghb.next]   ; Advance to next block
	test	eax,eax
	jz	@@done
	mov	_si,_ax
IFDEF	__16__
	shr	eax,16
	mov	es,ax
ENDIF
	jmp	@@findloop

@@done: mov	[largestblock],edx
	cmp	[memamount],ecx 		; All memory in heap?
	jne	@@heapcorr			; heap corrupt!
	xor	_ax,_ax
	ret

@@heapcorr:
	mov	_ax,errCardHeapCorrupted
	ERROR	ID_gusCoreFree
	ret
ENDP

;/***************************************************************************\
;*
;* Function:	gusDefragment
;*
;* Description: Defragments the GUS memory
;*
;\***************************************************************************/
PROC	gusDefragment _funct
	ret
ENDP


;* $Log: gus.asm,v $
;* Revision 1.2  1996/08/04 11:27:42  pekangas
;* All functions now preserve _bx
;*
;* Revision 1.1  1996/05/22 20:49:33  pekangas
;* Initial revision
;*


END