;***************************************************************************
;*	Driver pour la GUS
;*
;* Programm‚ par S‚bastien Granjoux
;* Commenc‚ le 07/01/95
;* Modification le 07/01/95

IDEAL
P386N

INCLUDE "CRYSLOAD.INC"
INCLUDE "CRYSERR.INC"
INCLUDE "CRYSDEV.INC"

PUBLIC	USEGUS

SEGMENT CODE PARA PUBLIC USE16 'CODE'

ASSUME cs:CODE,ds:CODE

;*************************************************************************
;*	Fonction de detection du driver

PROC	USEGUS

	call	USEDEVICE
	DEVICE <4,'ULTRASNDs=p,d,i$',OFFSET setgus,OFFSET startgus,OFFSET stopgus,OFFSET makegus,OFFSET defbpmgus,OFFSET peekgus,0,0,0>

ENDP

RAMPDELAY	EQU	4A9h

Compteur	DD	0
Compte		DD	0
BPMspeed	DW	0

STRUC GUSDATA

	action		DB	?
			DW      ?
			DB      ?
	note		DW	?
	volume		DW	?
	repadr		DD	?
	endadr		DD	?
	curadr		DD	?
			DD	?
			DD	?
			DD	?

ENDS

GusMem	DD	0
GusSample DD MAX_INST DUP (?)	; Adresse des samples dans la m‚moire de la GUS

;***************************************************************************
;*	Routine permettant d'‚crire un octet dans la GUS
;*
;* Entr‚e:
;*	DX	adresse de port de la GUS
;*	EBX	adresse o— ‚crire
;*	AL	donn‚ … ‚crire

PROC	putgus

	push	cx

	mov	cl,al
	mov	al,43h
	out	dx,al

	inc	dx
	mov	eax,ebx
	out	dx,ax

	dec	dx
	mov	al,44h
	out	dx,al

	add	dx,2
	shr	eax,16
	out	dx,al

	add	dx,2
	mov	al,cl
	out	dx,al

	sub	dx,4

	pop	cx

	ret

ENDP

;***************************************************************************
;*	Routine permettant de lire un octet de la RAM de la GUS
;*
;* Entr‚e:
;*	DX	adresse de port de la GUS
;*	EBX	adresse … lire
;*
;* Sortie:
;*	AL	octet lus

PROC	getgus

	mov	al,43h
	out	dx,al

	inc	dx
	mov	eax,ebx
	out	dx,ax

	dec	dx
	mov	al,44h
	out	dx,al

	add	dx,2
	shr	eax,16
	out	dx,al

	add	dx,2
	in	al,dx

	sub	dx,4

	ret

ENDP

;***************************************************************************
;*	Envoit une commande … la GUS
;*
;* Entr‚e:
;*	DX	adresse de port de la GUS
;*	AL	commande … envoyer
;*	AH	argument de la commande

PROC	putcom


	out	dx,al

	add	dx,2
	mov	al,ah
	out	dx,al

	sub	dx,2

	ret

ENDP

;***************************************************************************
;*	Routine d'initialisation de la GUS

PROC	setgus

	add	[(DEVICE ptr ds:si).port],103h
	mov	dx,[(DEVICE ptr ds:si).port]

	mov	ax,4ch		;Reset de la GUS
	call	putcom

	add	dx,4
	REPT	14
	in	al,dx
	ENDM
	sub	dx,4

	mov	ax,14ch
	call	putcom

	add	dx,4
	REPT    14
	in	al,dx
	ENDM
	sub	dx,4

	xor	ebx,ebx		; Test la m‚moire de la GUS
@@test_mem:

	mov	al,0AAh		; Ecrit 55AAh en 00000h
	call	putgus
	inc	ebx
	mov	al,055h
	call	putgus

	call	getgus		; Lit 16 bits en 00000h
	mov	ch,al
	dec	ebx
	call	getgus
	mov     cl,al

	add	ebx,256*1024
	cmp	ebx,(1024+256)*1024
	je	@@enough_mem
	cmp	cx,55aah
	je	@@test_mem

@@enough_mem:
	sub	ebx,256*1024
	mov	[GusMem],ebx
	jnz	@@find_gus

	mov	ax,GUS_NOT_FOUND	;Pas de gus
	stc
	ret

@@find_gus:

	mov	[ds:MixRate],4400
	mov	[(DEVICE PTR ds:si).irq],0
	mov	[(DEVICE ptr ds:si).port],dx
	mov	[ds:OFFSET port_gus1+1],dx
	mov	[ds:OFFSET port_gus2+1],dx

	mov	ax,0041h	; couche le DMA
	call	putcom

	mov	ax,0045h	; couche les timers
	call	putcom

	mov	ax,0049h	; pas de sampling
	call	putcom

	mov	ax,0cd0eh
	call	putcom

	mov	[ds:MixRate],4410
	mov	al,SIZE GUSDATA			;32*nombre de voix
	mov	cl,[byte ptr ds:OFFSET NbVoice]
	mul	cl
	mov	[ds:VoicesLen],ax

	push	si

	mov	si,OFFSET Instrument1
	mov	di,OFFSET GusSample+4
	xor	ebx,ebx
	mov	cl,MAX_INST
@@next_inst:
	push	cx

	mov	[ds:di],ebx
	push	di
	push	si

	mov	ax,[(INSTRUMENT ptr ds:si).adrseg]
	mov	es,ax
	xor	di,di
	mov	cx,[(INSTRUMENT ptr ds:si).length]

	or	cx,cx
	je	@@no_inst

	add	cx,SAMPLE_BORDER

	xor	si,si
@@next_byte:
	lods	[byte ptr es:si]
	call	putgus
	inc	ebx
	dec	cx
	jne	@@next_byte

@@no_inst:
	pop	si
	pop	di
	pop	cx

	add	si,SIZE INSTRUMENT
	add	di,4

	dec	cl
	jne	@@next_inst

	cmp	ebx,[GusMem]
	ja	@@not_enough_mem

	mov	ax,ds
	mov	es,ax
	mov	di,OFFSET VolumeTab	; remplit la table de volume
	mov	dx,7			; valeur intial
@@fill_tab:

	mov	ax,dx
	or	ah,ah
	mov	ah,15
	jnz	@@find_dec
@@next_dec:
	dec	ah
	shl	al,1
	jnc	@@next_dec
@@find_dec:
	shl	ax,4
	stosw
	add	dx,4			; coefficiant
	cmp	di,OFFSET VolumeTab+65*2
	jbe	@@fill_tab
	pop	si


	clc

	ret

@@not_enough_mem:

	mov	ax,NOT_ENOUGH_GUS
	pop	bx
	stc
	ret
ENDP



;***************************************************************************
;*	Commence … envoyer les donn‚es sur la GUS

PROC startgus

	mov	dx,[(DEVICE ptr ds:si).port]

	mov	ch,31
@@next_voices:
	dec	dx
	mov	al,ch
	out	dx,al
	inc	dx

	mov	ax,0800h	; 800h loop enable
	call	putcom		; voice mode

	mov	cl,ch
	inc	cl
	and	cl,1
	mov	ax,030ch
	jz	@@right
	mov	ah,0Ch
@@right:
	call	putcom		; pan register 3 et 12

	mov	ax,20dh		; stop les volumes ramps
	call	putcom

	mov	ax,3f06h	;changement de volume rapide
	call	putcom

	mov	al,9
	out	dx,al		; volume … 0
	inc	dx
	xor	ax,ax
	out	dx,ax
	dec	dx

	dec	ch
	jge	@@next_voices

	mov	ax,34ch		;Allume le haut parleur
	call	putcom

	sub	dx,103h
	mov	al,1h
	out	dx,al
	add	dx,103h

	mov	[word ptr cs:OFFSET gus_switch+1],OFFSET soundgus1-OFFSET gus_switch+3

	cli
	mov	ax,OFFSET soundgus
	mov	dx,cs
	xor	bl,bl
	call	setirq

	mov	al,00110110b  ;00110110b
	out     43h,al

	mov	ax,[BPMspeed]		; interruption a 50Hz
	out     40h,al
	rol     ax,8
	out     40h,al
	rol	ax,8

	movzx	ecx,ax
	mov	eax,80000000h
	xor	edx,edx
	div	ecx
	add	eax,eax
	mov	[ds:Compteur],eax
	mov	[ds:Compte],eax

	call	setautoeoi
	sti

	ret

ENDP

;***************************************************************************
;*	Proc‚dure qui place les donn‚es du son dans le buffer

PROC	makegus FAR

	mov	cl,[NbVoice]
	mov	di,OFFSET Voice1
	mov	si,[ds:OFFSET SoundPage]
@@next_voice:
	push	cx

	mov     dx,[(VOICE ptr ds:di).effet]
	call    dx

	mov	bx,[(VOICE ptr ds:di).play]
	add	bx,bx
	mov	dx,[word ptr ds:bx+OFFSET Notes]
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).note],dx

	mov  	al,[MasterVol]
	and	al,[(VOICE ptr ds:di).mute]
	mul	[(VOICE ptr ds:di).volume]
	mov	bl,ah
	xor	bh,bh
	shl	bx,1
	mov	ax,[OFFSET VolumeTab+bx]
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).volume],ax

	mov	bl,[(VOICE ptr ds:di).inst]
	and	bx,1fh
	mov	[(VOICE ptr ds:di).inst],bl
	shl	bx,2
	mov	ch,[(VOICE ptr ds:di).gusinf]
	mov	[(VOICE ptr ds:di).gusinf],0
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).action],ch

	test	ch,01h
	jz	@@no_loadadr1

	xor	eax,eax
	mov	ax,[(VOICE ptr ds:di).samplen]
	add	eax,[ds:bx+OFFSET GusSample]
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).endadr],eax

	xor	eax,eax
	mov	ax,[(VOICE ptr ds:di).samplen]
	sub	ax,[(VOICE ptr ds:di).replen]
	add	eax,[ds:bx+OFFSET GusSample]
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).repadr],eax

@@no_loadadr1:

	test	ch,02h
	jz	@@no_loadcur1

	mov	eax,[(dword ptr (VOICE ptr ds:di).adrvoc)]
	and	eax,0ffffh
	add	eax,[ds:bx+OFFSET GusSample]
	mov	[(GUSDATA ptr ds:si+OFFSET SoundBuf).curadr],eax

@@no_loadcur1:

	add	si,SIZE GUSDATA
	add	di,SIZE VOICE
	pop	cx
	dec	cl
	jne	@@next_voice

	and	si,65535-BUF_LEN
	mov	[word ptr ds:OFFSET SoundPage],si
	mov	[byte ptr cs:OFFSET switch_makemod],1Eh

	pop	edi
	pop	esi
	pop	ebp
	pop	es
	pop	ds

	ret


ENDP


;***************************************************************************
;*      interruption 8 permettant d'envoyer le son sur la gus

PROC    soundgus FAR

	push	eax
	push	bx
	push	cx
	push	dx

gus_switch:
	jmp	soundgus2

soundgus1:
	mov	ax,RAMPDELAY		; interruption … 1kHz
	out     40h,al
	mov     al,ah
	out     40h,al

	mov	[word ptr cs:OFFSET gus_switch+1],OFFSET soundgus2-OFFSET gus_switch+3

	mov	bx,[cs:OFFSET SoundPtr]

port_gus1:
	mov	dx,323h

	mov	cl,[byte ptr cs:OFFSET NbVoice]
	dec	cl
@@next_voice:
	dec	dx
	mov	al,cl
	out	dx,al

	inc	dx

	test	[(GUSDATA ptr cs:bx+OFFSET SoundBuf).action],4
	jz	@@no_voice_off

	mov	al,09h
	out	dx,al

	inc	dx
	mov	al,[cs:OFFSET VolumeTab]
	out	dx,al
	dec	dx

	mov	al,89h
	out	dx,al

	add	dx,2
	in	al,dx
	mov	ah,al
	sub	dx,2

	mov	al,7
	out	dx,al

	xor	al,al
	mov	ch,[cs:OFFSET VolumeTab+1]

	cmp	ch,ah
	ja	@@inc_vol
	mov	al,40h
	xchg	ch,ah
@@inc_vol:

	add	dx,2
	xchg	al,ah
	out	dx,al
	sub	dx,2

	mov	al,8
	out	dx,al

	add	dx,2
	mov	al,ch
	out	dx,al
	sub	dx,2

	mov	al,0dh
	out	dx,al

	add	dx,2
	mov	al,ah
	out	dx,al
	sub	dx,2

@@no_voice_off:

	add	bx,SIZE GUSDATA

	dec	cl
	jns	@@next_voice

	pop	dx
	pop	cx
	pop	bx

	dec	[word cs:OFFSET Compte+2]
	jz	@@int08

	pop	eax
	iret

@@int08:
	mov	eax,[cs:Compteur]
	add	[cs:OFFSET Compte],eax
	pop	eax
	jmp	[dword ptr cs:OldIrq]


soundgus2:
	mov	ax,[cs:BPMspeed]	; interruption … 50Hz
	out     40h,al
	mov     al,ah
	out     40h,al

	mov	[word ptr cs:OFFSET gus_switch+1],OFFSET soundgus1-OFFSET gus_switch+3

	mov	bx,[cs:OFFSET SoundPtr]

port_gus2:
	mov	dx,323h

	mov	cl,[byte ptr cs:OFFSET NbVoice]
	dec	cl
@@next_voice2:
	dec	dx
	mov	al,cl
	out	dx,al

	inc	dx

	mov	ch,[(GUSDATA ptr cs:bx+OFFSET SoundBuf).action]

	test	ch,1
	jz	@@no_new_inst

	mov	al,05h
	out	dx,al

	inc	dx
	mov	eax,[(GUSDATA ptr cs:bx+OFFSET SoundBuf).endadr]
	rol	eax,9
	out	dx,ax
	dec	dx

	mov	al,04h
	out	dx,al

	inc	dx
	ror	eax,16
	out	dx,ax
	dec	dx

	mov	al,03h
	out	dx,al

	inc	dx
	mov	eax,[(GUSDATA ptr cs:bx+OFFSET SoundBuf).repadr]
	rol	eax,9
	out	dx,ax
	dec	dx

	mov	al,02h
	out	dx,al

	inc	dx
	ror	eax,16
	out	dx,ax
	dec	dx

@@no_new_inst:

	test	ch,2
	jz	@@no_loadcur

	mov	al,0
	out	dx,al

	add	dx,2
	mov	al,3
	out	dx,al
	sub	dx,2

	REPT	6
	in	al,dx
	ENDM

	add	dx,2
	mov	al,3
	out	dx,al
	sub	dx,2

	mov	al,0bh
	out	dx,al

	inc	dx
	mov	eax,[(GUSDATA ptr cs:bx+OFFSET SoundBuf).curadr]
	shl	eax,9
	out	dx,ax
	dec	dx

	mov	al,0ah
	out	dx,al

	inc	dx
	ror	eax,16
	out	dx,ax
	dec	dx

	mov	al,0
	out	dx,al

	add	dx,2
	mov	al,8
	out	dx,al
	sub	dx,2

	REPT	6
	in	al,dx
	ENDM

	add	dx,2
	mov	al,8
	out	dx,al
	sub	dx,2


@@no_loadcur:

	test	ch,8
	jnz	@@ramp_vol

	jmp	@@ok_vol

@@ramp_vol:

	mov	al,89h
	out	dx,al

	add	dx,2
	in	al,dx
	mov	ah,al
	sub	dx,2

	mov	al,7
	out	dx,al

	xor	al,al
	mov	ch,[byte ptr ((GUSDATA ptr cs:bx+OFFSET SoundBuf).volume)+1]

	cmp	ch,ah
	ja	@@inc_vol2
	mov	al,40h
	xchg	ch,ah
@@inc_vol2:

	add	dx,2
	xchg	al,ah
	out	dx,al
	sub	dx,2

	mov	al,8
	out	dx,al

	add	dx,2
	mov	al,ch
	out	dx,al
	sub	dx,2

	mov	al,09h
	out	dx,al

	inc	dx
	mov	al,[byte ptr ((GUSDATA ptr cs:bx+OFFSET SoundBuf).volume)]
	out	dx,al
	dec	dx

	mov	al,0dh
	out	dx,al

	add	dx,2
	mov	al,ah
	out	dx,al
	sub	dx,2

@@ok_vol:
	mov	al,1
	out	dx,al

	inc	dx
	mov	ax,[(GUSDATA ptr cs:bx+OFFSET SoundBuf).note]
	out	dx,ax
	dec	dx

	add	bx,SIZE GUSDATA

	dec	cl
	jns	@@next_voice2

	and	bx,65535-BUF_LEN
	mov	[cs:OFFSET SoundPtr],bx

	pop	dx
	pop	cx
	pop	bx
	pop	eax

	jmp	IMAKEMOD


ENDP


;***************************************************************************
;*	cette routine permet d'arreter l'envoit du son sur la gus

PROC	stopgus

	mov	dx,[(DEVICE ptr ds:si).port]

	mov	ax,14ch		;Eteint le haut parleur
	call	putcom

	sub	dx,103h
	mov	al,3
	out	dx,al
	add	dx,103h

	mov	cl,31
@@next_voices:
	dec	dx
	mov	al,cl
	out	dx,al
	inc	dx

	mov	ax,0300h	; 800h loop enable
	call	putcom		; voice mode


	mov	ax,030dh		; stop les volumes ramps
	call	putcom

	dec	cl
	jns	@@next_voices

	cli
	mov     al,00110110b
	out     43h,al
	xor	al,al
	out     40h,al
	out     40h,al

	call	reseteoi

	ret

ENDP

;***************************************************************************
;*	Change la vitesse en BPM
;*
;* Entr‚e:
;*	AL	nouvelle vitesse

PROC	defbpmgus

	movzx	cx,al
	mov	ax,8426h	;1193180*2.5
	mov	dx,2dh
	div	cx
	mov	[BPMspeed],ax
	sub	[BPMspeed],RAMPDELAY

	movzx	ecx,ax
	mov	eax,80000000h
	xor	edx,edx
	div	ecx
	add	eax,eax
	mov	[ds:Compteur],eax
	mov	[ds:Compte],eax

	ret

ENDP

;***************************************************************************
;*	R‚cupŠre un l'octet jou‚ par la GUS
;*
;* Entr‚e:
;*	AL	numero de la voix

PROC	peekgus

	mov	dx,[(DEVICE ptr ds:bx).port]
	dec	dx
	inc	al
	cli
	out	dx,al
	inc	dx

	dec	al
	mov	ah,SIZE VOICE
	mul	ah
	mov	bx,ax
	add	bx,OFFSET Voice1
	push	bx

	mov	al,8Ah
	out	dx,al

	inc	dx
	in	ax,dx
	mov     bx,ax
	shl	ebx,16
	dec	dx

	mov	al,8Bh
	out	dx,al

	inc	dx
	in	ax,dx
	mov	bx,ax
	dec	dx

	shr	ebx,9

	mov	al,43h
	out	dx,al

	inc	dx
	mov	ax,bx
	out	dx,ax
	dec	dx

	mov	al,44h
	out	dx,al
	add	dx,2

	shr	ebx,16
	mov	ax,bx
	out	dx,al

	add	dx,2
	in	al,dx
	sub	dx,4
	sti

	pop	bx
	mov	dl,[(VOICE PTR ds:bx).volume]
	mul	dl

	shr	ax,6

	ret

ENDP

ENDS

END