;**************************************************************************
;*	Player de mod, cela fait office d'exemple d'utilisation en
;*	assembleur d'utilisation des routines et vous permet de juger de
;*	la qualit‚ (ou des d‚fauts) des routines de CRYSTAL...
;*
;* Programm‚ par S‚bastien Granjoux
;* Commenc‚ en f‚vrier 93
;* DerniŠre modif 10/04/95

IDEAL

P386N
MODEL	SMALL

PUBLIC	main

VERNB	EQU	"2.70"
DATE	EQU	"12/05/95"

EXTRN	SetMode3:near
EXTRN	ResetMode:near
EXTRN	SetPage:near
EXTRN	PutCpkBlk:near
EXTRN	PutText:near
EXTRN	PutVu:near

EXTRN	GetMixRate:near
EXTRN	GetVoiceByte:near
EXTRN	GetName:near
EXTRN	GetTempo:near
EXTRN	GetModInf:near
EXTRN	GetPattern:near
EXTRN	GetVoice:near

EXTRN	USESPK:far
EXTRN	USEGUS:far
EXTRN	USESP:far
EXTRN	USESB:far
EXTRN	USEDAC:far
EXTRN	USEADL:far
EXTRN	CHANGEVOL:far
EXTRN	DETECTSND:far
EXTRN	FLOADMOD:far
EXTRN	UNLOADMOD:far
EXTRN	SETMOD:far
EXTRN	MAKEMOD:far
EXTRN	STARTMOD:far
EXTRN	STOPMOD:far
EXTRN	TOGGLEVOC:far
EXTRN	LOCKMOD:far
EXTRN	SETMODPOS:far
EXTRN	GETMODPOS:far

INCLUDE "CRYSERR.INC"
INCLUDE "CRYSID.INC"

DEV_NAME_LEN	EQU	32
NB_SAMPLE	EQU	31

DATASEG

PROGRAM_MSG	DB 'Crystal Player V',VERNB,' Par S‚bastien Granjoux',09h,09h,09h,09h,DATE,0dh,0ah
		DB 'Ce programme permet de jouer des fichiers MOD.',0ah,0dh
		DB 'Il s''utilise de la maniŠre suivante :',0dh,0ah,0dh,0ah
		DB 09h,'CRYS [/SK|/AD|/DCl|/SBp,i|/GSp] [/FRf] nom du fichier mod',0dh,0ah
		DB 0ah,0dh
		DB 09h,'  /SK permet d''utiliser le speaker',0dh,0ah
		DB 09h,'  /AD permet d''utiliser une carte adlib',0dh,0ah
		DB 09h,'  /DCl permet d''utiliser un DAC sur le port parallŠle l(1-4)',0dh,0ah
		DB 09h,'  /SBp,i,d permet d''utiliser une soundblaster (8 bits mono) sur le port',0dh,0ah
		DB 09h,'           p avec l''irq i et le canal DMA d',0dh,0ah
		DB 09h,'  /SPp,i,d permet d''utiliser une soundblaster pro (8 bits stereo) sur',0dh,0ah
		DB 09h,'           le port p avec l''irq i et le canal DMA d',0dh,0ah
		DB 09h,'  /GSp permet d''utiliser une GUS sur le port p',0dh,0ah
		DB 09h,'  /FRf permet de donner la fr‚quence de mixage en disaine de Hz',0dh,0ah
		DB 0dh,0ah,'Par d‚faut le player recherche dans les variables d''environnement la carte',0dh,0ah
		DB 'sonore … utiliser ou les paramŠtres manquant et joue vers 20Khz.',0dh,0ah
		DB 0dh,0ah
		DB '$'

MAIN_SCR:
	DB      27,2,'Crystal Player Version ',VERNB,' Programm‚ par '
	DB	'S‚bastien Granjoux',11,8,'le ',DATE,10
MUSIC_INF:
	DB	01,23,' Musique: ',27,69 DUP (' '),10
DEVICE_INF:
	DB	23,' Sur: ',27,DEV_NAME_LEN DUP (' '),23,'A ',27,'00000 Hz'
	DB	23,'  Temps: ',27,'00:00',10
MODULE_INF:
	DB	23,' Sequence: ',27,'000/000',23,'  Pattern: ',27,'000'
	DB	23,'  Ligne: ',27,'00',23,'  Tempo: ',27,'00/000'
	DB	23,'  Volume: ',27,'000',10
EXTRA_INF:
	DB	23,' M‚moire utilis‚: ',27,'000 Ko'
	DB	23,'  CPU utilis‚: ',27,'00 %',10
VUMETER_SCR:
	DB	25,'Ú',12,4,'ÄÂ',12,24,'ÄÂ',12,4,'ÄÂ'
	DB	12,5,'ÄÂ',12,18,'ÄÂ',12,18,'Ä¿',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB	25,'³',30,11,4,25,'³',31,11,24,25,'³ ',19,'00',25,' ³'
	DB	26,11,5,25,'³',21,11,18,25,'³ ',16,12,16,'þ',25,' ³',10
	DB      25,'À',12,4,'ÄÁ',12,24,'ÄÁ',12,4,'ÄÁ'
	DB	12,5,'ÄÁ',12,18,'ÄÁ',12,18,'ÄÙ',10,10
	DB	2,'  ',26,'F1 ',27,'Aide  ',26,'F2 ',27,'Vu-metres  '
	DB	26,'F3 ',27,'Instruments  ',11,26
	DB	26,'Esc ',27,'Quitter',11,2
	DB	0

SAMPLE_SCR:
	DB	25,'Ú',12,78,'Ä¿',10
FIRST_SAMPLE:
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB	25,'³',11,6,27,32 DUP (' '),11,2,32 DUP (' '),25,11,6,'³',10
	DB      25,'À',12,78,'ÄÙ',10,0

HELP_SCR:
	DB	25,'Ú',12,78,'Ä¿',10
	DB	25,'³',27,11,29,'Touches utilisables',11,30,25,'³',10
	DB	25,'³',27,11,28,12,21,'Ä',11,29,25,'³',10
	DB	25,'³',27,11,34,12,9,'Ä',11,35,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,'Pause       : Pause                    ',11,39,25,'³',10
	DB	25,'³',27,'Arret Defil : Arret sur un pattern     ',11,39,25,'³',10
	DB	25,'³',27,'1 .. 0      : Voix on/off              ',11,39,25,'³',10
	DB	25,'³',27,'Fin         : Passer au pattern suivant',11,39,25,'³',10
	DB	25,'³',27,'+/-         : Changer le volume global ',11,39,25,'³',10
	DB	25,'³',27,'Curseur     : Changer de position      ',11,39,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB	25,'³',27,11,78,25,'³',10
	DB      25,'À',12,78,'ÄÙ',10,0

LIST_EFFET	DB 'ARPEGGIO        '
		DB 'PORTAMENTO UP   '
		DB 'PORTAMENTO DOWN '
		DB 'TONE PORTAMENTO '
		DB 'VIBRATO         '
		DB 'PORTAMENTO+SLIDE'
		DB 'VIBRATO+SLIDE   '
		DB 'TREMOLO         '
		DB 'PHASOR EFFECT   '
		DB 'PLAY SAMPLE PART'
		DB 'VOLUME SLIDE    '
		DB 'POSITION JUMP   '
		DB 'SET VOLUME      '
		DB 'PATTERN BREAK   '
		DB '                '
		DB 'SET SPEED       '
		DB 'SET BPM         '
		DB 'TREMOR          '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB '                '
		DB 'SET FILTER      '
		DB 'FINE PORT UP    '
		DB 'FINE PORT DOWN  '
		DB 'SET GLISSANDO   '
		DB 'SET VIBRATO     '
		DB 'SET FINETUNE    '
		DB 'LOOP            '
		DB 'SET TREMOLO     '
		DB 'STOP            '
		DB 'RETTRIG SAMPLE  '
		DB 'FINE SLIDE UP   '
		DB 'FINE SLIDE DOWN '
		DB 'NOTE CUT        '
		DB 'NOTE DELAY      '
		DB 'PATTERN DELAY   '
		DB '                '

LIST_NOTE	DB 'C-0','C#0','D-0','D#0','E-0','F-0'
		DB 'F#0','G-0','G#0','A-0','A#0','B-0'
		DB '   '

FIRST_ERROR	EQU	1
LAST_ERROR	EQU	22

UNKNOW_ERROR	DB	'Erreur inconnu$'
ERROR1		DB	'Fonction inconnu$'
ERROR2		DB	'Fichier introuvable$'
ERROR3		DB	'Chemin introuvable$'
ERROR4		DB	'Trop de fichier ouvert$'
ERROR5		DB	'AccŠs refus‚$'
ERROR6		DB	'Handle inconnu$'
ERROR7		DB	'Bloc de contr“le m‚moire d‚truit$'
ERROR8		DB	'M‚moire insuffisante$'
ERROR9		DB	'Adresse m‚moire incorrect$'
ERROR10		DB	'Environnement incorrect$'
ERROR11		DB	'Trop de pattern$'
ERROR12		DB	'Sample trop grand$'
ERROR13		DB	'Port parallŠle introuvable$'
ERROR14		DB	'Sound blaster introuvable$'
ERROR15		DB	'GUS introuvable$'
ERROR16		DB	'Pas assez de m‚moire sur la GUS$'
ERROR17		DB	'Drivers introuvable$'
ERROR18		DB	'Voix inconnu$'
ERROR19		DB	'Carte son introuvable$'
ERROR20		DB	'Carte Adlib introuvable$'
ERROR21		DB	'Trop de drivers charg‚$'

MSG_ERROR	DW	OFFSET ERROR1
		DW	OFFSET ERROR2
		DW	OFFSET ERROR3
		DW	OFFSET ERROR4
		DW	OFFSET ERROR5
		DW	OFFSET ERROR6
		DW	OFFSET ERROR7
		DW	OFFSET ERROR8
		DW	OFFSET ERROR9
		DW	OFFSET ERROR10
		DW	OFFSET ERROR11
		DW	OFFSET ERROR12
		DW	OFFSET ERROR13
		DW	OFFSET ERROR14
		DW	OFFSET ERROR15
		DW	OFFSET ERROR16
		DW	OFFSET ERROR17
		DW	OFFSET ERROR18
		DW	OFFSET ERROR19
		DW	OFFSET ERROR20
		DW	OFFSET ERROR21
		DW	OFFSET PROGRAM_MSG

Old_int9	DD 0

Device		DW THE_BEST
Port		DW 0
Irq		DB 0
Dma		DB 0
MixSpeed	DW 0
MasterVol	DB 63
ModVoice	DB 0
StartTime	DD 0
StartMem	DW 0
VBLChrono	DD 1
PlayChrono	DD 1
LastChrono	DB 0
Mode		DB 2
VuMeters	DB 32 DUP (0)
CutVoices	DW 0
SeqLength	DB 0

BIOS_SEG	EQU	40h
BIOS_CHRONO	EQU	6Ch

USINGMSG	DB	'Utilise $'
DEVICEMSG	DW	OFFSET NOS_DEV,0
		DW	OFFSET SPK_DEV,0
		DW	OFFSET DAC_DEV,1
		DW	OFFSET SB_DEV,4
		DW	OFFSET GUS_DEV,2
		DW	OFFSET SP_DEV,4
		DW	OFFSET ADL_DEV,0

NOS_DEV		DB	'rien$'
SPK_DEV		DB	'le haut parleur interne$'
DAC_DEV		DB	'un DAC$'
SB_DEV		DB	'une SoundBlaster$'
GUS_DEV		DB	'une Gravis Ultra Sound$'
SP_DEV		DB	'une SoundBlaster Pro$'
ADL_DEV		DB	'une carte adlib $'

ADRMSG		DB	' … l''adresse $'
PORTMSG		DB	' sur le port parallŠle $'
IRQMSG		DB	' avec l''IRQ $'
DMAMSG		DB	' et le DMA 1$'
NEWLINE		DB	0ah,0dh,'$'
GUSMSG		DB	'Chargement des samples ...',0ah,0dh,'$'

DevFrq		DW	0,1900,2200,2200,0,2200,1700

Buffer:
Filename	DB 128+4 DUP (0)

ExecBlk		DW	0
		DW	OFFSET CmdLine
		DW	SEG CmdLine
		DD	-1
		DD	-1

CmdLine		DB	00,0Dh
OldStack	DD	0
ComSpecMsg	DB	'COMSPEC',0
DefaultCom	DB	'C:\COMMAND.COM',0

CODESEG

PROC	main

	mov	ax,es		; Recup‚rer la place d‚j… prise par le player
	dec	ax
	mov	es,ax
	mov	cx,[es:3]
	inc	ax
	mov	es,ax
	mov	ah,48h		; R‚cup‚rer la m‚moire libre
	mov	bx,0FFFFh
	int	21h
	add	bx,dx
	mov	[StartMem],bx

	push	es
	push	ds

	call	Usegus
	call	Usesp
	call	Usesb
	call	Usedac
	call	Usespk
	call	Useadl

	pop	es
	pop	ds
	mov	si,80h
	mov	di,OFFSET Filename
	call	getarg
	push	es
	pop	ds
	cmp	al,1
	jae	@@mod_file_ok
	mov	ax,BAD_ARG
	jmp	error
@@mod_file_ok:

	mov	si,OFFSET Filename
	mov	di,si
	mov	bx,OFFSET Irq
@@get_arg:
	lodsb
	cmp	al,'-'
	je	@@get_switch
	cmp	al,','
	je	@@get_sup
	or	al,al
	je	@@line_used

@@get_file:
	stosb
	lodsb
	or	al,al
	jne	@@get_file
	jmp	@@get_arg

@@get_sup:
	mov	ax,BAD_ARG
	cmp	bx,OFFSET Dma
	ja	error
	call	get_dec
	jc	@@no_sup
	mov	[bx],al
@@no_sup:
	inc	si
	inc	bx
	jmp	@@get_arg

@@get_switch:
	mov	bx,OFFSET Irq
	lodsw
	cmp	ax,'RF'
	je	@@get_freq
	cmp	ax,'BS'
	je	@@get_sb
	cmp	ax,'KS'
	je	@@get_spk
	cmp	ax,'CD'
	je	@@get_dac
	cmp	ax,'SG'
	je	@@get_gus
	cmp	ax,'PS'
	je	@@get_sp
	cmp	ax,'DA'
	je	@@get_adlib
	cmp	ax,'LN'
	je	@@get_null
	mov	ax,BAD_ARG
	jmp	error
@@get_freq:
	call	get_dec
	inc	si
	mov	[MixSpeed],ax
	jmp	@@get_arg
@@get_spk:
	mov	[Device],PC_SPEAKER
	lodsb
	or	al,al
	je	@@get_arg
	mov	ax,BAD_ARG
	jmp	error
@@get_dac:
	mov	[Device],DAC_ON_LPT
	jmp	@@get_device
@@get_sb:
	mov	[Device],SOUNDBLASTER
	jmp	@@get_device
@@get_sp:
	mov	[Device],SOUNDPRO
	jmp	@@get_device
@@get_gus:
	mov	[Device],GUS
	jmp	@@get_device
@@get_null:
	mov	[Device],NO_DEVICE
	jmp	@@get_device
@@get_adlib:
	mov	[Device],ADLIB
@@get_device:
	call	get_hex
	inc	si
	jc	@@get_arg
	mov	[Port],ax
	jmp	@@get_arg

@@line_used:
	xor	al,al
	stosb

	push    ds
	push	OFFSET Device
	push	ds
	push	OFFSET Port
	push	ds
	push	OFFSET Irq
	push	ds
	push	OFFSET Dma
	call	Detectsnd
	jnc	@@ok
	cmp	ax,DRV_NOT_FOUND
	je	error
@@ok:

	mov	di,OFFSET Filename
	xor	al,al
	repne	scasb
	dec	di
	mov	eax,[es:di-4]
	and	eax,0DFDFDFFFh
	cmp	eax,'DOM.'
	je	@@loadmod
	sub	di,4
	mov	cx,4
	mov	al,'.'
	repne	scasb
	je	@@loadmod
	mov	[byte ptr es:di],al
	mov	[dword ptr es:di+1],'DOM'

@@loadmod:
	push	ds
	push	OFFSET Filename
	call    far Floadmod
	jc	error

@@loadok:

	call	putcard		; Affiche la carte utilis‚

	call	PutMusInf

	cmp	[MixSpeed],0
	jne	@@keep_freq
	mov	bx,[Device]
	add	bx,bx
	mov	ax,[OFFSET DevFrq+bx]
	mov	[MixSpeed],ax
@@keep_freq:
	push	[MixSpeed]
	push	[word ptr ds:OFFSET Device]
	push	[word ptr ds:OFFSET Port]
	push	[word ptr ds:OFFSET Irq]
	push	[word ptr ds:OFFSET Dma]

	call	far Setmod
	jc	error

	call	PutDevInf

	call	setmode3

	mov	si,OFFSET MAIN_SCR
	xor	dx,dx
	call	PutCpkBlk

	push	es		; Sauvegarde l'interruption clavier
	mov	ax,3509h
	int	21h
	mov	[word ptr ds:OFFSET Old_int9],bx
	mov	[word ptr ds:OFFSET Old_int9+2],es
	pop	es
	push	ds		; Utilise une interruption clavier simplifi‚e
	mov	ax,cs
	mov	ds,ax
	cli
	mov	dx,OFFSET hitkey
	mov	ax,2509h
	int	21h
	sti
	pop	ds

	call	far MakeMod
	call	far MakeMod

	mov	bx,16
	call	waitVBL
test_loop:
	push	bx

	call    PutModInf

	cmp	[Mode],2
	jmp	$+2

	mov	al,[cs:Key]
	or	al,al
	je	@@loop_end
	cmp	al,1
	je	@@loop_end
	cmp	al,17
	jbe	@@loop_end
	cmp	al,61
	je	@@loop_end
	cmp	al,60
	je	@@loop_end
	cmp	al,59
	je	@@loop_end
	cmp	al,69
	je	@@loop_end
	cmp	al,79
	je	@@loop_end
	cmp	al,70
	je	@@loop_end
	cmp	al,78
	je	@@loop_end
	cmp	al,74
	je	@@loop_end
	jmp	@@loop_end
@@loop_end:
	xor	ecx,ecx
	call	waitVBL
	add	[VBLChrono],ecx
	pop	bx
	dec	bx
	jne	test_loop


	call	far StartMod
	call	GetTime
	mov	[ds:StartTime],eax

	call	WaitVBL
main_loop:
	push	bx

	call	far MakeMod
	call	far MakeMod

	call	PutModInf

	cmp	[Mode],2
	jne	@@no_refresh
	call	PutVuMeter
@@no_refresh:

	mov	al,[cs:Key]
	or	al,al
	je	@@end_main
	cmp	al,1
	je	@@exit
	cmp	al,17
	jbe	@@mute
	cmp	al,61
	je	@@sample
	cmp	al,60
	je	@@vumeter
	cmp	al,59
	je	@@help
	cmp	al,69
	je	@@pause
	cmp	al,63
	je	@@dosshell
	cmp	al,79
	je	@@end_pat
	cmp	al,70
	je	@@lock_pat
	cmp	al,78
	je	@@plus
	cmp	al,74
	je	@@minus
	cmp	al,75
	je	@@backward
	cmp	al,77
	je	@@forward

@@end_main:
	test	[cs:Mode],80h
	jnz	@@no_chrono
	xor	ecx,ecx
	call	waitVBL
	mov	eax,[PlayChrono]
	shr	eax,4
	sub	eax,ecx
	sub	[PlayChrono],eax

@@no_chrono:
	pop	bx
	jmp	main_loop

@@help:
	mov	[cs:Key],0
	and	[Mode],0F0h
	or	[Mode],1
	mov	si,OFFSET HELP_SCR
	mov	dx,0500h
	call	PutCpkBlk
	jmp	@@end_main

@@sample:
	mov	[cs:Key],0
	and	[Mode],0F0h
	or	[Mode],3
	mov	si,OFFSET SAMPLE_SCR
	mov	dx,0500h
	call	PutCpkBlk
	jmp	@@end_main

@@vumeter:
	mov	[cs:Key],0
	and	[Mode],0F0h
	or	[Mode],2
	mov	si,OFFSET VUMETER_SCR
	mov	dx,0500h
	call	PutCpkBlk
	call	PutVuMeter
	jmp	@@end_main

@@pause:
	mov	[cs:Key],0
	xor	[Mode],80h
	test	[Mode],80h
	jz	@@unpause
	call	far StopMod
	jmp	@@end_main
@@unpause:
	call	far StartMod
	jmp	@@end_main

@@mute:
	mov	[cs:Key],0
	sub	al,2
	push	ax
	mov	cl,al
	mov	ax,1
	shl	ax,cl
	xor	[ds:CutVoices],ax
	call	TOGGLEVOC
	jmp     @@end_main

@@lock_pat:
	mov	[cs:Key],0
	call 	LOCKMOD
	jmp	@@end_main

@@end_pat:
	mov	[cs:Key],0
	call    GETMODPOS
	and	ax,0FFC0h
	add	ax,64
	movzx	bx,[ds:SeqLength]
	shl	bx,6
	cmp	ax,bx
	je	@@end_main
	push	ax
	call	SETMODPOS
	jmp	@@end_main

@@plus:
	mov	[cs:Key],0
	cmp	[ds:MasterVol],63
	je	@@end_main
	inc	[ds:MasterVol]
	push	[word ptr ds:MasterVol]
	call	CHANGEVOL
	jmp	@@end_main

@@minus:
	mov	[cs:Key],0
	cmp	[ds:MasterVol],0
	je	@@end_main
	dec	[ds:MasterVol]
	push	[word ptr ds:MasterVol]
	call	CHANGEVOL
	jmp	@@end_main

@@forward:
	mov	[cs:Key],0
	call	GETMODPOS
	inc	ax
	movzx	bx,[ds:SeqLength]
	shl	bx,6
	cmp	ax,bx
	je	@@end_main
	push	ax
	call	SETMODPOS
	jmp	@@end_main

@@backward:
	mov	[cs:Key],0
	call	GETMODPOS
	or	ax,ax
	je	@@end_main
	dec	ax
	push	ax
	call	SETMODPOS
	jmp	@@end_main

@@dosshell:
	push	es
	mov	ah,62h
	int	21h
	mov	es,bx
	mov	es,[es:2Ch]
	mov	si,OFFSET ComSpecMsg
	xor	di,di
	mov	cx,0ffffh
@@search_comspec:
	rep	cmpsb
	cmp	[byte ptr ds:si-1],0
	jne	@@next_string
	cmp	[byte ptr es:di-1],'='
	je	@@find_comspec
@@next_string:
	mov	si,OFFSET ComSpecMsg
	xor	al,al
	repne	scasb
	cmp	[byte ptr es:di],0
	jne	@@search_comspec

	push	ds
	pop	es
	mov     di,OFFSET DefaultCom

@@find_comspec:
	push	ds
	mov	[word ptr cs:OFFSET OldStack+2],ss
	mov	[word ptr cs:OFFSET OldStack],sp

	push	ds		; Reprend l'ancienne interruption clavier
	cli
	mov	ax,2509h
	lds	dx,[dword ptr ds:OFFSET Old_int9]
	int	21h
	sti
	pop	ds

	call	resetmode

	push	es
	push	ds
	pop	es
	pop	ds
	mov	ax,4B00h
	mov	bx,OFFSET ExecBlk
	mov	dx,di
	int	21h

	lss	sp,[cs:OFFSET OldStack]
	pop	ds
	pop	es

	call	putcard		; Affiche la carte utilis‚

	call	setmode3

	mov	si,OFFSET MAIN_SCR
	xor	dx,dx
	call	PutCpkBlk

	push	ds
	push	cs
	pop	ds
	mov	dx,OFFSET hitkey
	mov	ax,2509h
	cli
	int	21h
	sti
	pop	ds
	mov	[cs:Key],0
	and	[Mode],0F0h
	or	[Mode],2

	jmp	@@no_chrono

@@exit:

	call	far Stopmod

	push	ds		; Reprend l'ancienne interruption clavier
	cli
	mov	ax,2509h
	lds	dx,[dword ptr ds:OFFSET Old_int9]
	int	21h
	sti
	pop	ds

	call	resetmode

	call	Unloadmod

	mov	ax,4c00h
	int	21h

error:
	cmp	ax,256
	jbe	@@dos_error
	sub	ax,246
@@dos_error:
	mov	bx,_DATA
	mov	ds,bx
	sub	ax,FIRST_ERROR
	jb	@@unknow
	cmp	ax,LAST_ERROR
	ja	@@unknow
	mov	bx,ax
	shl	bx,1
	mov     dx,[ds:bx+MSG_ERROR]
	mov	ah,09h
	int	21h
	mov	ax,bx
	shl	bx,1
	mov	ah,4ch
	int	21h

@@unknow:
	mov	dx,OFFSET UNKNOW_ERROR
	mov	ah,09h
	int	21h
	mov	ax,4cffh
	int	21h

ENDP

;***************************************************************************
;*	Convertit un nombre sous forme de chaine de caractŠre en ds:si
;*	en binaire dans ax,ds:si pointant sur la fin du nombre
;*
;* Entr‚e:
;*	DS:SI	repr‚sentation d‚cimal du nombre
;* Sortie:
;*	AX	nombre
;*	C=1	si la chaine n'est pas un nombre

PROC	get_dec

	xor	ax,ax
	xor	dh,dh
	mov	dl,[ds:si]
	sub	dl,'0'
	jl	@@error
	cmp	dl,9
	ja	@@error

@@next_digit:
	inc	si

	lea	ax,[eax*4+eax]
	shl	ax,1
	add     ax,dx

	mov	dl,[ds:si]
	sub	dl,'0'
	jl	@@no_digit
	cmp	dl,9
	jbe	@@next_digit

@@no_digit:
	clc
	ret
@@error:
	stc
	ret
ENDP

;***************************************************************************
;*	Convertit un nombre sous forme de chaine de caractŠre en ds:si
;*	en binaire dans ax,ds:si pointant sur la fin du nombre
;*
;* Entr‚e:
;*	DS:SI	repr‚sentation hexad‚cimal du nombre
;* Sortie:
;*	AX	nombre
;*	C=1	si la chaine n'est pas un nombre

PROC	get_hex

	xor	ax,ax
	xor	dh,dh
	mov	dl,[ds:si]
	sub	dl,'0'
	jl	@@error
	cmp	dl,9
	jbe	@@next_digit
        and	dl,1fh
	xor	dl,18h
	inc	dl
	cmp	dl,0fh
        ja	@@error
@@next_digit:
	inc	si

	shl	ax,4
	add     ax,dx

	mov	dl,[ds:si]
	sub	dl,'0'
	jl	@@no_digit
	cmp	dl,9
	jbe	@@next_digit
	and	dl,1fh
        xor	dl,18h
	inc	dl
	cmp	dl,0fh
        jbe	@@next_digit
@@no_digit:
	clc
	ret
@@error:
	stc
	ret
ENDP

;***************************************************************************
;*	Convertie un nombre en DL en une chaine de caractŠre repr‚sentant
;*	ce nombre en d‚cimal
;*
;* Entr‚e:
;*	DS:DI	adresse de la zone o— mettre la chaine sur 3 octet
;*	AX	nombre … convertir
;*	CX	nombre de chiffre (1,10,100,1000,10000)

PROC	DecToStr

	xor	dx,dx
	div	cx
	or	al,al
	jne	@@number
	sub	al,'0'-' '
@@number:
	add	al,'0'
	mov	[ds:di],al
	inc	di
	push	dx
	xor	dx,dx
	mov	ax,cx
	mov	cx,10
	div	cx
	or	ax,ax
	mov	cx,ax
	pop	ax
	je	@@exit

@@next:
	xor	dx,dx
	div	cx
	or	al,al
	add	al,'0'
	mov	[ds:di],al
	inc	di
	push	dx
	xor	dx,dx
	mov	ax,cx
	mov	cx,10
	div	cx
	or	ax,ax
	mov	cx,ax
	pop	ax
	jne	@@next

@@exit:

	ret

ENDP


Key	DB	0

;***************************************************************************
;*      interruption qui remplace l'interruption 09, elle est trŠs simple
;*	pour ‚viter que le programme reste trop longtemps avec les
;*	interruption coup‚es.

PROC    hitkey FAR

	push    ax

	in      al,60h
	or      al,al
	js      @@relache

	mov     [cs:OFFSET Key],al

	mov     al,61h
	out     20h,al

	pop     ax
	iret

@@relache:
	mov     al,61h
	out     20h,al

	pop     ax
	iret

ENDP


SCREEN_POS	DW 640

;***************************************************************************
;*	Ecrit un nombre en EAX sur 32 bits en chaine de caractŠre
;*	correspondant … se repr‚sentation hexad‚cimal
;*
;* Entr‚e:
;*	EDX	nombre … convertir

PROC	writelong	FAR

	push	es
	mov	ax,0b800h
	mov	es,ax
	mov	di,[cs:SCREEN_POS]
	add	di,16

	mov	ax,'h'
	std
	stosb

	mov	cl,4
@@next_digit:
	movzx	ax,dl
	shr	edx,8
	shl	ax,4
	shr	al,4
	xchg	al,ah
	and	ax,0f0fh
	add	ax,3030h
	cmp	al,3Ah
	jb	@@al_ok
	add	al,7
@@al_ok:
	cmp	ah,3Ah
	jb	@@ah_ok
	add	ah,7
@@ah_ok:
	xchg	al,ah
	dec	di
	stosb
	dec	di
	mov	al,ah
	stosb
	dec	cl
	jne	@@next_digit

	cld

	add	di,19
	mov	al,' '
	stosb
	inc	di

	pop	es

	cmp	di,4000
	jb	@@ok
	mov	di,0
@@ok:
	mov	[cs:SCREEN_POS],di

	ret

ENDP



;***************************************************************************
;*	Cette routine s‚pare les arguments de la ligne de commande par un
;*	zero,deplus les switchs sont mis en majuscule.Les switchs doivents
;*	commencer par '-','/' ou '+' et le '/' est toujours transform‚ en '-'
;*	deplus les virgules se retrouve toujours en d‚but de mots.Enfin
;*	si une chaine est mis entre '"' elle n'est pas modifi‚.
;*
;* Entr‚e:
;*	DS:SI	adresse de la ligne de commande
;*      ES:DI	adresse du buffer devant contenir la ligne modifi‚
;* Sortie:
;*	AL	nombre d'arguments sans les switchs

PROC	getarg

	lodsb
	mov	cl,al
	xor	ch,ch

@@search_letter:
	dec	cl
	jl	@@nothing_left
	lodsb
	cmp	al,' '
	je	@@search_letter
	cmp	al,09
	je	@@search_letter
	inc	ch
	cmp	al,'+'
	je	@@switch
	cmp	al,'-'
	je	@@switch
	cmp	al,'/'
	jne	@@letter
	mov	al,'-'
@@switch:
	stosb
	dec	ch

@@majuscule:
	dec	cl
	jl	@@nothing_left
	lodsb
	mov	ah,al
	and	ah,0dfh
	cmp	ah,'A'
	jb	@@search_white
	cmp	ah,'Z'
	ja	@@search_white
	mov	al,ah
	stosb
	jmp	@@majuscule

@@string:
	stosb
	dec	cl
	jl	@@nothing_left
	lodsb
	cmp	al,'"'
	jne	@@string

@@letter:
	stosb
	lodsb
	dec	cl
	jl	@@nothing_left

@@search_white:
	cmp	al,'"'
	je	@@string
	cmp	al,' '
	je	@@find_white
	cmp	al,09
	je	@@find_white
	cmp	al,','
	jne	@@letter
	mov	ah,al
	xor	al,al
	stosw
	cmp	[byte ptr ds:si],','
        jne	@@search_letter
	stosb
	jmp	@@search_letter

@@find_white:
	xor	al,al
	stosb
	jmp	@@search_letter

@@nothing_left:
	xor	ax,ax
	stosw
	mov	al,ch

	ret

ENDP

;**************************************************************************
;*	Affiche une ligne de texte donnant la carte sonore utilis‚
;*
;* Attention:
;*	Les variables Device,Port,Irq et DMA doivent ˆtre d‚finis ainsi
;*	que des messages

PROC	putcard

	mov	ah,09h
	mov	dx,OFFSET USINGMSG
	int	21h

	mov	bl,[byte ptr ds:OFFSET Device]
	xor	bh,bh
	shl	bx,2
	add	bx,OFFSET DEVICEMSG
	mov	dx,[ds:bx]
	mov	ah,09h
	int	21h

	mov	cl,[ds:bx+2]
	or	cl,cl
	je	@@thats_all

	mov	ah,09h
	mov	dx,OFFSET ADRMSG
	dec	cl
	jne 	@@adr_msg
	mov	dx,OFFSET PORTMSG
@@adr_msg:
	int	21h

	mov	bx,[ds:Port]
	or	bh,bh
	je	@@no_first_digit

	mov	dl,bh
	add	dl,'0'
	mov	ah,02h
	int	21h

	mov	dl,bl
	shr	dl,4
	and	dl,0fh
	add	dl,'0'
	cmp	dl,'9'
	jbe	@@no_hexa
	add	dl,7
@@no_hexa:
	mov	ah,02h
	int	21h

	and	bl,0fh
@@no_first_digit:
	mov	dl,bl
	add	dl,'0'
	mov	ah,02h
	int	21h

	dec	cl
	jle	@@thats_all

	mov	ah,09h
	mov	dx,OFFSET IRQMSG
	int	21h

	mov	dl,[ds:Irq]
	add	dl,'0'
	mov	ah,02h
	int	21h

	dec	cl
	jle	@@thats_all

	mov	ah,09h
	mov	dx,OFFSET DMAMSG
	int	21h

@@thats_all:

	mov	ah,09h
	mov	dx,OFFSET NEWLINE
	int	21h

	cmp	[byte ptr ds:Device],4
	je	@@loadsamp

	ret
@@loadsamp:

	mov	ah,09h
	mov	dx,OFFSET GUSMSG
	int	21h

	ret

ENDP

;****************************************************************************
;*	Rafraichie les informations aprŠs un LOADMOD

PROC	PutMusInf

	push	ds
	pop	es
	mov	di,OFFSET MUSIC_INF+13
	mov	bl,0
	call	GetName

	call	GetModInf
	mov	[ModVoice],ah
	mov	[SeqLength],al
	mov	cx,100
	xor	ah,ah
	mov	di,OFFSET MODULE_INF+17
	call	DecToStr

	mov	dl,1
	mov	di,OFFSET FIRST_SAMPLE+5
@@next_sample:
	mov	bl,dl
	push	di
	call    GetName
	pop	di
	add	di,34
	add	dl,15
	mov	bl,dl
	push	di
	call	GetName
	pop	di
	add	di,42
	sub	dl,14
	cmp	dl,NB_SAMPLE/2+1
	jne	@@next_sample

	mov	ah,48h
	mov	bx,0FFFFh
	int	21h
	mov	ax,[ds:StartMem]
	sub	ax,bx
	mov	cx,100
	shr	ax,6
	mov	di,OFFSET EXTRA_INF+20
	call	DecToStr

	ret

ENDP

;****************************************************************************
;*	Rafraichie les informations aprŠs un SETMOD
;*

PROC	PutDevInf

	mov	bl,[byte ptr ds:OFFSET Device]
	xor	bh,bh
	shl	bx,2
	add	bx,OFFSET DEVICEMSG
	mov	si,[ds:bx]
	mov	di,OFFSET DEVICE_INF+8

	push	es
	push	ds
	pop	es
	mov	cx,DEV_NAME_LEN
@@next_char:
	lodsb
	cmp	al,'$'
	je	@@fill_with_space
	stosb
	dec	cx
	jne	@@next_char
@@fill_with_space:
	mov	al,' '
	rep	stosb

	mov	cx,10000
	call	GetMixRate
	lea	ax,[eax*4+eax]
	add	ax,ax
	mov	di,OFFSET DEVICE_INF+44
	call	DecToStr

	pop	es
	ret

ENDP

;****************************************************************************
;*	Lit le compteur horaire du BIOS

PROC	GetTime

	push	ds
	mov	ax,BIOS_SEG
	mov	ds,ax
	mov	eax,[ds:BIOS_CHRONO]
	pop	ds
	mov	[ds:StartTime],eax

	ret

ENDP

;****************************************************************************
;*	Rafraichissement des informations aprŠs un MAKEMOD
;*	Le rafraichissement se fait directement … l'ecran.

PROC	PutModInf

	mov	cx,100
	movzx	ax,[ds:MasterVol]
	mov	di,OFFSET Buffer
	call	DecToStr
	mov	si,OFFSET Buffer
	mov	cl,3
	mov	dx,0344h
	call	PutText


	call	GetTempo
	push	ax
	mov     di,OFFSET Buffer
	xor	ah,ah
	mov	cx,10
	call	DecToStr
	mov	si,OFFSET Buffer
	mov	cl,2
	mov	dx,0334h
	call	PutText

	pop	ax
	mov	al,ah
	mov     di,OFFSET Buffer
	xor	ah,ah
	mov	cx,100
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	cl,3
	mov	dx,0337h
	call	PutText

	call	GetPattern
	push	dx
	push	ax
	mov	di,OFFSET Buffer
	xor	ah,ah
	mov	cx,100
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dx,031Dh
	mov	cl,3
	call	PutText

	pop	ax
	mov	al,ah
	mov	di,OFFSET Buffer
	xor	ah,ah
	mov	cx,100
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dx,030Bh
	mov	cl,3
	call	PutText

	pop	ax
	mov	di,OFFSET Buffer
	xor	ah,ah
	mov     cx,10
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dx,0329h
	mov	cl,2
	call	PutText

	push	ds
	mov	ax,BIOS_SEG
	mov	ds,ax
	mov	eax,[ds:BIOS_CHRONO]
	pop	ds
	sub	eax,[ds:StartTime]
	xor	dx,dx
	mov	bx,1092
	div	bx
	push	dx
	mov	di,OFFSET Buffer
	mov	cx,10
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dx,0239h
	mov	cl,2
	call	PutText

	pop	ax
	xor	dx,dx
	shl	ax,4
	mov	bx,291
	div	bx
	mov	di,OFFSET Buffer
	mov	cx,10
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	cmp	[byte ptr ds:si],' '
	jne	@@no_0
	mov	[byte ptr ds:si],'0'
@@no_0:
	mov	dx,023Ch
	mov	cl,2
	call	PutText


	mov	eax,[ds:PlayChrono]
	lea	eax,[eax*4+eax]
	lea	eax,[eax*4+eax]
	shl	eax,2
	xor	edx,edx
	div	[ds:VBLChrono]
	sub	ax,100
	neg	ax
	mov	bl,al
	inc	bl
	sub	bl,[ds:LastChrono]
	cmp	bl,2
	jae	@@no_hyster
	mov	al,[ds:LastChrono]
@@no_hyster:
	mov	[ds:LastChrono],al
	xor	ah,ah
	cmp	al,100
	jb	@@under100
	xor	al,al
@@under100:
	mov	cx,10
	mov	di,OFFSET Buffer
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dx,0427h
	mov	cl,2
	call	PutText

	ret
ENDP

;****************************************************************************
;*	Rafraichie les vu metres
;*

PROC	PutVuMeter

	mov	bl,[ModVoice]
	dec	bl
@@next_voice:
	call    GetVoice
	push	cx
	push	dx

	mov	al,dl
	xor	ah,ah
	mov	di,OFFSET Buffer
	mov	cx,10
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dh,bl
	add	dh,6
	mov	dl,2
	mov	cl,2
	call	PutText

	pop	ax
	push	ax

	dec	al
	mov	si,OFFSET FIRST_SAMPLE+5
	jns	@@no_null_samp
	xor	al,al
	add	si,76*15
@@no_null_samp:
	and	al,1Fh
	cmp	al,15
	jb	@@first_row
	add	si,34
	sub	al,15
@@first_row:
	mov	dl,76
	mul	dl
	add	si,ax
	mov	dh,bl
	add	dh,6
	mov	dl,7
	mov	cl,22
	call	PutText

	pop	ax
	shr	ax,8
	shl	ax,4
	mov	si,ax
	add	si,OFFSET LIST_EFFET
	mov	dh,bl
	add	dh,6
	mov	dl,43
	mov	cl,16
	call	PutText

	pop	ax
	push	ax
	mov	bh,al
	and	ax,0Fh
	lea	ax,[eax*2+eax]
	mov	si,ax
	add	si,OFFSET LIST_NOTE
	mov	dh,bl
	add	dh,6
	mov	dl,37
	mov	cl,3
	call	PutText
	shr	bh,4
	or	bh,bh
	je	@@no_oct
	add	[es:di-2],bh
@@no_oct:

	pop	ax
	shr	ax,8
	mov	di,OFFSET Buffer
	mov	cx,10
	call	DecToStr
	mov	[byte ptr ds:di],0
	mov	si,OFFSET Buffer
	mov	dh,bl
	add	dh,6
	mov	dl,32
	mov	cl,2
	call	PutText

	mov     ax,1
	mov	cl,bl
	shl	ax,cl
	test	[CutVoices],ax
	jnz	@@mute_voice
	call	GetVoiceByte
	xor	bh,bh
	mov	ah,[ds:OFFSET VuMeters+bx]
	shr	ah,2
	shr	al,2
	sub	al,ah
	add	[ds:OFFSET VuMeters+bx],al
	mov	cl,[ds:OFFSET VuMeters+bx]
	shr	cl,4
	mov	ch,16
	mov	dh,bl
	add	dh,6
	mov	dl,62
	call	PutVu
@@mute_voice:

	dec	bl
	jns	@@next_voice

	ret

ENDP

VER_RETRACE	EQU	3dah

;****************************************************************************
;*	Attend une VBL en comptant dans CX

PROC	WaitVBL

		mov	dx,VER_RETRACE
@@wait1:
		inc	ecx
		in	al,dx
		test	al,8
		jnz	@@wait1

@@wait2:
		inc	ecx
		in	al,dx
		test	al,8
		jz	@@wait2

		ret
ENDP

ENDS

STACK	400h

END

