; ****************************************************************************
; tmodplay.asm (for MSDOS)
; ----------------------------------------------------------------------------
; TMODPLAY.COM ! MOD PLAYER program ! by Erdogan TAN
;
; 09/09/2017
;
; [ Last Modification: 30/09/2017 ]
;
; Modified from 'tinyplay.s' (TINYPLAY.PRG) source code by Erdogan Tan
; (12/05/2017)
;
; Derived from source code of 'MODPLAY.ASM' (TYNYPL211) by Carlos Hasan
; (21/06/1994), Tiny Mod Player v2.11 (RUNME.EXE)
;
; Derived from source code of 'SB16MOD.COM' (SB16MOD.ASM) by Silvio Turello
; (11/02/1995), Sound Blaster 16 Mod Player
;
; Assembler: NASM 2.11
; ----------------------------------------------------------------------------
;	   nasm  tmodplay.asm -l tmodplay.lst -o TMODPLAY.COM	
; ****************************************************************************

[BITS 16]
[org 100h]

;=============================================================================
; TMODPLAY.COM (MSDOS) program code starts here !!!
;=============================================================================

; Method Reference 1: TINYPLAY.COM (Tiny Mod player v1, 04/03/2017)
; Method Reference 2: EX0.C (TNYPL211, 21/06/1994)

Start:
	push	cs ; ?
	pop	es ; ?
	mov	di, bss_start
	mov	cx, bss_end
	sub	cx, di
	shr	cx, 2
	xor	eax, eax
	rep stosd

GetFileName:				; Parse  the Command line...
	mov	si, 80h
	mov	bl, [si]
	;xor	bh, bh
	inc	bx
	mov	byte [si+bx], 0		; make AsciiZ filename.
	inc	si
ScanName:       
	lodsb
	test	al, al
	jz	short pmsg_usage
	cmp	al, 20h
	je	short ScanName		; scan start of name.
	mov	di, si
	dec	di
ScanPeriod:
	lodsb
	cmp	al, '.'			; if period NOT found,
	je	short PrintMesg		; then add a .MOD extension.
	test	al, al
	jnz	short ScanPeriod
	dec	si
SetExt:
	;mov	byte [si+0], '.'
	;mov	byte [si+1], 'M'
	;mov	byte [si+2], 'O'
	;mov	byte [si+3], 'D'
	mov	dword [si], '.MOD'
	mov	byte [si+4], 0
PrintMesg:
	mov     ax, 0900h		; Prints the Credits Text.
	;lea    dx, [Credits]
	mov	dx, Credits
	int     21h

	call    MODDetectCard		; Detect the SB Addr, Irq.
	jnc	short a_3
Fail:  
	; Print Failed Message,
	; and exit.
	mov     ax, 0900h		; yes, print error and Exit.
	;lea    dx, [MsgNotFound]
	mov	dx, MsgNotFound
	int     21h
	jmp     short Exit
Exit:
	mov     ax, 4C00h		; Bye!
	int     21h
here:
	jmp	short here

pmsg_usage:
	mov     ax, 0900h		; Prints the Usage Text.
	;lea    dx, [msg_usage]
	mov	dx, msg_usage
	int     21h
	jmp	short Exit
a_3:
Success:        
	;mov	dx, [Port]		; Print Successful message.
	;mov	cl, [IRQ]
	;mov	ch, [DRQ]

	mov	[SbPort], dx
	shr     dl, 4
	add     dl, '0'
	mov     [PortText], dl
	mov	[SbIrq], cl
	add     cl, '0'
	mov     [IrqText], cl
	mov	[SbDma], ch
	add	ch, '0'
	mov     [DmaText], ch

	mov     ax, 0900h
	;lea    dx, [MsgFound]
	mov	dx, MsgFound
	int     21h

LoadMod:  
	; es:di = mod file name address (in PSP area)
	call    MODLoadModule ; Load the MODule...

	or	ax, ax
	jnz	short a_4

	mov     ax, 0900h
	;lea    dx, [MsgModErr]
	mov	dx, MsgModErr
	int     21h
	jmp	short Exit
a_4:
	mov	byte [playing], 0 ; 30/09/2017
PlayNow:
	; <<< 'EX0.C' >>>
	;void main(void)
	;{
    	;Module *Song;
    	;word Port;
    	;byte IRQ,DRQ;
	;
    	;if (MODDetectCard(&Port,&IRQ,&DRQ)) {
        ;   printf("Sound Blaster not found.\n");
        ;   return;
    	;}
    	;printf("Sound Blaster found at Addr:%03x IRQ:%d DMA:%d\n",Port,IRQ,DRQ);
    	;if ((Song = MODLoadModule("TUNE.MOD")) != NULL) {
        ;   if (MODPlayModule(Song,8,22222,Port,IRQ,DRQ,PM_TIMER))
	;   	printf("Error initializing the sound system.\n");
        ;   else {
        ;   	printf("Playing music...\n");
        ;   	FadeInMusic();
        ;   	TestSample();
        ;   	FadeOutMusic();
        ;   	MODStopModule();
        ;   }
        ;   MODFreeModule(Song);
    	;}
    	;else {
        ;   printf("Error loading modulefile.\n");
    	;}
    	;return;
	;}
 
	PM_TIMER  equ 0
	PM_MANUAL equ 1

	mov     si, ax ; Song `; Module structure

; setup the sound card driver

	mov     ax, 22222 ; [Rate]

	;mov	bx, [si+module.numtracks] 
	mov	bl, [si+module.numtracks] ; [Chans]
	mov	bh, PM_TIMER ; [mode]

	mov     dx, [SbPort] ; [Port], [ioaddr]
	mov     cl, [SbIrq]  ; [IRQ], [irqnum]	
	mov     ch, [SbDma]  ; [DRQ], [drqnum]	
		
	call    MODPlayModule
	;jnc	short a_5

	and	ax, ax
	jz	short a_5  ; ax = 0 --> Success

	; ax = -1 --> Error

	mov     ax, 0900h
	;lea    dx, [MsgInitErr]
	mov	dx, MsgInitErr
	int     21h
	jmp	short Exit
a_5:
;Playing: 	
	mov     ax, 0900h
	;lea    dx, [PlayMsg]
	mov	dx, PlayMsg
	int     21h
a_6:
	call	MODPoll ; 29/09/2017
CheckAnyKey:
	mov     ah, 1			; any key pressed?
	int     16h			; no, Loop.
	jz	short a_7

	mov     ah, 0			; flush key buffer...
	int     16h

	call	MODStopModule
		
	mov     ax, 0900h
	;lea    dx, [OkMsg]
	mov	dx, OkMsg
	int     21h

	jmp	Exit	
a_7:
	dec	byte [KeyWait]				
	jz	short CheckAnyKey
		
	nop
	nop
	nop

	jmp	short a_6

	db	0
PlayMsg:
	db	0Dh, 0Ah
	db	"Playing music... "
	db	"$"
OkMsg:
	db	0Dh, 0Ah
	db	"OK."
	db	0Dh, 0Ah, "$"
KeyWait:	
	db	0

;*****************************************************************************
; MODPLAY.ASM (21/06/1994) in 'TNYPL211'
;*****************************************************************************
;		Tiny MOD Player for Watcom C/C++32 and DOS/4GW
;		      Version 2.11a June 5th, 1994
;
;		      Copyright 1993,94 Carlos Hasan
;*****************************************************************************

;=============================================================================
; equates
;=============================================================================

MAXVOICES equ 8			; number of voices
DMABUFLEN equ 1024		; DMA buffer length (multiple of 64)
VOLBUFLEN equ 66*256		; volume table length
MIXBUFLEN equ DMABUFLEN+2048	; mixing/boosting buffer length
TIMERRATE equ 17000		; timer interrupt rate in ticks

; 25/09/2017
PATTBUFOFF equ DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15
PATTBUFLEN equ 128*2048

; Functions:
;	MODPlayModule
;	MODStopModule
;	MODPlaySample
;	MODStopSample
;	MODSetPeriod
;	MODSetVolume
;	MODSetMusicVolume
;	MODSetSampleVolume
;	MODDetectCard
;	MODPoll
;	MODVoiceTable

;=============================================================================
; structures
;=============================================================================

struc module	; module (song) structure
  .numtracks:	resw 1  ; number of tracks
  .orderlen:	resw 1	; order length
  .orders:	resb 128 ; order list
  .patterns:	resd 128 ; pattern addresses
  .sampptr:	resd 32 ; sample start addresses
  .sampend:	resd 32	; sample end addresses
  .samploop:	resd 32	; sample loop point addresses
  .sampvolume:	resb 32	; sample default volumes
  .size:
endstruc

struc sample	; sample structure
  .period:	resw 1	; default period
  .volume:      resw 1  ; default volume
  .datalen:     resd 1	; sample data length
  .dataptr:     resd 1	; sample data address
  .size:
endstruc

struc track	; track structure
  .note:	resw 1	; note index
  .period:	resw 1	; period value
  .inst:	resb 1	; instrument
  .volume:	resb 1	; volume
  .effect:      resw 1	; effect
  .destperiod:	resw 1	; toneporta wanted period
  .tonespeed:	resb 1	; toneporta speed
  .vibparm:	resb 1	; vibrato depth/rate
  .vibpos:	resb 1	; vibrato wave position
  .tremparm:	resb 1	; tremolo depth/rate
  .trempos:	resb 1	; tremolo wave position
  		resb 1	; alignment
  .arptable	resw 3	; arpeggio periods
  .size:	
endstruc

;=============================================================================
; Module Player stuff
;=============================================================================

;-----------------------------------------------------------------------------
; MODPlayModule - start playing a music module
; In:
;  Song  = module address
;  Chans = number of channels
;  Rate  = playback rate
;  Port  = port address
;  irq   = irq number
;  dma   = dma channel
;  mode  = polling mode
;-----------------------------------------------------------------------------
MODPlayModule: 
; Song:dword,Chans:dword,Rate:word,Port:word,IRQ:word,DRQ:byte,Mode:byte
	
	;INPUT:
	;ax = 22222 ; [Rate]
 	;bl = [si+module.numtracks] ; [Chans]
	;bh = PM_TIMER ; [mode]
	;dx = [SbPort] ; [Port], [ioaddr]
	;cl = [SbIrq]  ; [IRQ], [irqnum]	
	;ch = [SbDma]  ; [DRQ], [drqnum]
	;si = offset Song
	
	;pushad
	;push	es

; setup the music module address

	;les	si, [Song]

	mov     [moduleptr], si
	;mov	[moduleptr+2], ds

; setup the sound card driver

	;mov	ax, [Rate]
	;mov	bl, [Chans]
	;mov	dx, [Port]
	;mov	cl, [IRQ]
	;mov	ch, [DRQ]
	;mov	bh, [Mode]

	call    mixinit
	jc      playmoduled0

push es
mov bx, 0B800h
mov es, bx
xor bx, bx
mov byte [es:bx+4], "*"
mov byte [es:bx+5], 4Eh
pop es

; build the period to pitch table (16.16 fixed point values)

	movzx   ebx, ax ; [Rate], 22222
	mov     eax, 8363*428
	xor     edx, edx
	shld    edx, eax, 16
	shl     eax, 16
	div     ebx
	mov     esi, eax
	;lea	di, [pitchtable]
	mov	di, pitchtable
	mov     cx, 3425
	xor     bx, bx
playmodulel0:
	inc     bx
	xor     edx, edx
	mov     eax, esi
	div     ebx
	mov     [di], eax
	add     di, 4
	loop    playmodulel0

; setup global volumes for music and sample channels
	
	mov     byte [musicvolume], 255
	mov     byte [samplevolume], 255

; clear the module player track structures

        ;push	es
        ;mov	ax, ds
        ;mov	es, ax
	;cld
	;lea	di, [tracks]
	mov	di, tracks
	mov     cx, MAXVOICES * track.size
	xor     al, al
	rep     stosb
	;pop	es

; check if there is a module to playback

	xor     ax, ax
	mov     [numtracks], ax

	mov     si, [moduleptr]
	;or	si, [moduleptr+2]
	test    si, si
	clc
	je      short playmoduled0

; setup player interpreter variables

	;les	si, [moduleptr]
	;mov	ax, [es:si+module.orderlen]
	mov     ax, [si+module.orderlen]
	mov     [orderlen], al
	;mov	ax, [es:si+module.numtracks]
	mov	ax, [si+module.numtracks]
	mov     [numtracks], ax
	mov     byte [tempo], 6
	mov     byte [bpm], 125
	mov     byte [orderpos], 0
	mov     byte [tempocount], 0
	mov     byte [pattrow], 40h

; setup the player callback timer routine

	;lea	dx, [pollmodule]
	mov	dx, pollmodule
	call    mixsettimerproc
	mov     dl, [bpm]
	call    mixstarttimer
	clc

playmoduled0:
	;pop	es
	;popad
	sbb	ax, ax
	retn

;-----------------------------------------------------------------------------
; MODStopModule - shut down the music system
;-----------------------------------------------------------------------------
MODStopModule:
	pushad
	call    mixstoptimer	    ; stop the timer callback
	call    mixdone		 ; shutdown the SB stuff
	popad
	retn

;-----------------------------------------------------------------------------
; MODPoll - polls the music system in manual mode
;-----------------------------------------------------------------------------
MODPoll:
	pushad
	cmp     byte [manualmode], 0  ; call the polling routine only
	je      short modpolld0	      ; if we are using the manual
	cmp     byte [playing], 0     ; polling mode (and if the driver
	je      short modpolld0	      ; is active).
	call    mixpoll
modpolld0:
	popad
	retn

;-----------------------------------------------------------------------------
; MODPlaySample - play sample instrument
; In:
;  voice  = voice number
;  sample = sample address
;-----------------------------------------------------------------------------
MODPlaySample:
;; Voice:word,SamplePtr:word
;	pushad
;	push	es
;	;cli
;
;; get the voice number and track address
;
;	movzx	ebx, word [Voice]
;	les	di, [SamplePtr]
;	mov     si, bx
;	imul    si, track.size
;
; set the voice pitch value
;
;	movzx   eax, word [es:di+sample.period]
;	mov     eax, [4*eax+pitchtable]
;	mov     [4*ebx+voicepitch], eax
;
;; set the voice sample parameters
;
;	mov     eax, [es:di+sample.dataptr]
;	mov     [4*ebx+voicepos], eax
;	add     eax, [es:di+sample.datalen]
;	mov     [4*ebx+voiceend], ax
;	mov     [4*ebx+voiceloop], ax
;
;; set the voice and track volumes
;
;	mov     ax, [es:di+sample.volume]
;	mov     [si+tracks.volume], al
;	mul     byte [samplevolume]
;	mov     [4*ebx+voicevolume], ah
;
;	;sti
;	pop	es
;	popad
	retn

;-----------------------------------------------------------------------------
; MODStopSample - stop the playing sample
; In:
;  voice = voice number
;-----------------------------------------------------------------------------
MODStopSample:
;; Voice:word
;	pushad
;	;cli
;
;; get the voice number
;
;	movzx	ebx, word [Voice]
;	xor     eax, eax
;
;; clear the voice sample parameters
;
;	mov     [4*ebx+voicepos], eax
;	mov     [4*ebx+voiceend], ax
;	mov     [4*ebx+voiceloop], ax
;
;	;sti
;	popad
	retn

;-----------------------------------------------------------------------------
; MODSetPeriod -  set the voice period value
; In:
;  voice = voice number
;  period = period value (113-856)
;-----------------------------------------------------------------------------
MODSetPeriod:
;; Voice:word,Period:word
;	pushad
;	;cli
;
;; get the voice number and period value
;
;	movzx	ebx, word [Voice]
;	movzx	eax, word [Period]
;
;; set the voice pitch value
;
;	mov     eax, [4*eax+pitchtable]
;	mov     [4*ebx+voicepitch], eax
;
;	;sti
;	popad
	retn

;-----------------------------------------------------------------------------
; MODSetVolume -  set the voice volume level
; In:
;  voice = voice number
;  volume = volume level (0-64)
;-----------------------------------------------------------------------------
MODSetVolume:
;; Voice:word,Volume:word
;	pushad
;	;cli
;
;; get the voice number and track address
;
;	movzx	ebx, word [Voice]
;	mov     eax, [Volume]
;	mov     si, bx
;	imul    si, track.size
;
;; set the voice and track volume
;
;	mov     [si+tracks.volume], al
;	mul     byte [samplevolume]
;	mov     [4*ebx+voicevolume], ah
;
;	;sti
;	popad
	retn

;-----------------------------------------------------------------------------
; MODSetMusicVolume - set the global music volume
;-----------------------------------------------------------------------------
MODSetMusicVolume:
;; Volume:word
;	pushad
;	;cli
;
;; set new music volume
;
;	mov     ax, [Volume]
;	mov     [musicvolume], al
;
;; update all the music voices
;
;	mov	si, tracks
;	xor     ebx, ebx
;setmusicvolumel0:
;	mov     al, [si+track.volume]
;	mul     byte [musicvolume]
;	mov     [4*ebx+voicevolume], ah
;	add     si, track.size
;	inc     bx
;	cmp     bx, [numtracks]
;	jb      short setmusicvolumel0
;
;	;sti
;	popad
	retn

;-----------------------------------------------------------------------------
; MODSetSampleVolume - set the global sample volume
;-----------------------------------------------------------------------------
MODSetSampleVolume:
;; Volume:word
;	pushad
;	;cli
;
;; set the sample volume
;
;	mov     ax, [Volume]
;	mov     [samplevolume], al
;
;; update all the sample voices
;
;	mov	si, tracks
;	xor     ebx, ebx
;setsamplevolumel0:
;	cmp     bx, [numtracks]
;	jb      short setsamplevolumef0
;	mov     al, [si+track.volume]
;	mul     byte [samplevolume]
;	mov     [4*ebx+voicevolume], ah
;setsamplevolumef0:
;	add     si, track.size
;	inc     bx
;	cmp     bx, MAXVOICES
;	jb      short setsamplevolumel0
;
;	;sti
;	popad
	retn

;-----------------------------------------------------------------------------
; MODDetectCard - detect the Sound Blaster configuration
; Out:
;  Port = I/O Port
;  IRQ = IRQ level
;  DRQ = DMA channel
;-----------------------------------------------------------------------------
MODDetectCard:
; Port:dword,IRQ:dword,DRQ:dword

	;pushad
	;push es

; call the lowlevel autodetection routine

	call    mixdetect

; set the parameters in the user variables

	;les	di, [Port]
	;mov	[es:di], dx
	;les	di, [IRQ]
	;mov	[es:di], cl
	;les	di, [DRQ]
	;mov	[es:di], ch
	
	;pop	es
	;popad

	sbb     ax, ax
	retn

;-----------------------------------------------------------------------------
; pollmodule - polls the module player
;-----------------------------------------------------------------------------
pollmodule:
	; 29/09/2017 (MSDOS modifications)
	pushad
	push	es
	;push	gs
	dec     byte [tempocount]	; decrease the tempo counter
	jle     short pollmodulef0
	;lea	si, [tracks]
	mov	si, tracks		; while in the same pattern row
	xor     ebx, ebx		; update the track effects.
pollmodulel0:
	call    updatechannel
	add     si, track.size
	inc     bx
	cmp     bx, [numtracks]
	jb      short pollmodulel0
	;pop	gs
	pop	es
	popad
	retn

pollmodulef0:				; advance to the next pattern row.
	mov     al, [tempo]		; update the tempo counter
	mov     [tempocount], al
	xor	edx, edx
	;lgs	dx, [moduleptr]		; get the module and pattern address
	mov	dx, [moduleptr]
	les	di, [pattptr]
	cmp     byte [pattrow], 40h	; need to advance to the next order?
	jb      short pollmodulef2
	xor     eax, eax		; reset the pattern row
	mov     [pattrow], al
	mov     al, [orderpos]		; if we are at the end of the order
	cmp     al, [orderlen] 		; list, loop to the beginning
	jb      short pollmodulef1
	xor     al, al
	mov     [orderpos], al
pollmodulef1:
	inc     byte [orderpos]		; get the new pattern address
	;movzx	eax, byte [gs:edx+eax+module.orders]
	movzx   eax, byte [edx+eax+module.orders]
	;les	di, [gs:edx+4*eax+module.patterns]
	les	di, [edx+4*eax+module.patterns]
pollmodulef2:
	inc     byte [pattrow]		; increase pattern row number
	;lea	si, [tracks]
	mov     si, tracks
	xor     ebx, ebx		; read and interpret the next
pollmodulel1:				; pattern row of events
	call    readchannel
	add     si, track.size
	add     di, 4
	inc     bx
	cmp     bx, [numtracks]
	jb	short pollmodulel1
	mov     [pattptr], di		; save pattern row address
	mov	[pattptr+2], es
	;pop	gs
	pop	es
	popad
	retn

;-----------------------------------------------------------------------------
; readchannel - read the next note event from the pattern sheet
; In:
;  EBX = voice number
;  DS:SI = track address
;  ES:DI = pattern address
;  GS:EDX = module address
;-----------------------------------------------------------------------------
readchannel:
	; 29/09/2017 (MSDOS modifications)

	pushad

; check for new sample number . . .

	mov     al, [es:di+1]
	test    al, al
	je      short readchannelf0
	mov     [si+track.inst], al
	movzx   eax, al
	;mov	al, [gs:edx+eax+module.sampvolume]
	mov	al, [edx+eax+module.sampvolume]
	mov     [si+track.volume], al
	mul     byte [musicvolume]
	mov     [4*ebx+voicevolume], ah

; check for new note pitch . . .

readchannelf0:
	mov     al, [es:di]
	test    al, al
	je      short readchannelf1
	movzx   eax, al
	mov     [si+track.note], ax
	cmp     byte [es:di+3], 03h
	je      short readchannelf1
	mov     ax, [2*eax+periodtable]
	mov     [si+track.period], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	movzx   eax, byte [si+track.inst]
	lea     edx, [4*eax+edx]
	;mov	eax, [gs:edx+module.sampptr]
	mov     eax, [edx+module.sampptr]
	mov     [4*ebx+voicepos], eax
	;mov	eax, [gs:edx+module.sampend]
	mov     eax, [edx+module.sampend]
	mov     [4*ebx+voiceend], eax
	;mov	eax, [gs:edx+module.samploop]
	mov	eax, [edx+module.samploop]
	mov     [4*ebx+voiceloop], eax

; check the new track effect . . .

readchannelf1:
	mov     dx, [es:di+2]
	mov     [si+track.effect], dx
	movzx   eax, dh
	and     al, 0Fh
	call    [2*eax+efxtable]

	popad
	retn

;-----------------------------------------------------------------------------
; updatechannel - update the track using the current effect
; In:
;  EBX = voice number
;  ESI = track address
;-----------------------------------------------------------------------------
updatechannel:
	pushad
	mov     dx, [si+track.effect]
	movzx   eax, dh
	and     al, 0Fh
	call    [2*eax+efxtable2]
	popad
	retn

;=============================================================================
; Protracker effects stuff
;=============================================================================

;-----------------------------------------------------------------------------
; efxnull - dummy effect
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxnull:
	retn

;-----------------------------------------------------------------------------
; efxarpeggio - arpeggio
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxarpeggio:
	test    dl, dl
	je      short efxnull
	mov     dh, dl
	and     dl, 0Fh
	shr     dh, 4
	movzx   eax, word [si+track.note]
	mov     cx, [2*eax+periodtable]
	mov     [si+track.arptable], cx
	add     al, dh
	mov     cx,[2*eax+periodtable]
	mov     [si+2+track.arptable], cx
	sub     al, dh
	add     al, dl
	mov     cx, [2*eax+periodtable]
	mov     [si+4+track.arptable], cx
	retn
efxarpeggio2:
	test    dl, dl
	je      short efxnull
	movzx   eax, word [si+track.arptable]
	xchg    [si+4+track.arptable], ax
	xchg    [si+2+track.arptable], ax
	mov     [si+track.arptable], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn

;-----------------------------------------------------------------------------
; efxportaup - slides the pitch up
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxportaup:
	xor     dh, dh
	movzx   eax, word [si+track.period]
	sub     ax, dx
	cmp     ax, 28
	jge     short efxportaupf0
	mov     ax, 28
efxportaupf0:
	mov     [si+track.period], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn

;-----------------------------------------------------------------------------
; efxportadown - slides the pitch down
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxportadown:
	xor     dh, dh
	movzx   eax, word [si+track.period]
	add     ax, dx
	cmp     ax, 3424
	jle     short efxportadownf0
	mov     ax, 3424
efxportadownf0:
	mov     [si+track.period], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn

;-----------------------------------------------------------------------------
; efxtoneporta - tone portamento
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxtoneporta:
	test    dl, dl
	jne     short efxtoneportaf0
	mov     dl, [si+track.tonespeed]
efxtoneportaf0:
	mov     [si+track.tonespeed], dl
	mov     [si+track.effect], dx
	movzx   eax, word [si+track.note]
	mov     ax, [2*eax+periodtable]
	mov     [si+track.destperiod], ax
	retn
efxtoneporta2:
	xor     dh, dh
	movzx   eax, word [si+track.period]
	mov     cx, [si+track.destperiod]
	cmp     ax, cx
	je      efxnull
	jg      short efxtoneportaf1
	add     ax, dx
	cmp     ax, cx
	jle     short efxtoneportaf2
	mov     ax, cx
efxtoneportaf2:
	mov     [si+track.period], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn
efxtoneportaf1:
	sub     ax, dx
	cmp     ax, cx
	jge     short efxtoneportaf3
	mov     ax, cx
efxtoneportaf3:
	mov     [si+track.period], ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn

;-----------------------------------------------------------------------------
; efxvibrato - pitch vibrato
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxvibrato:
	mov     al, [si+track.vibparm]
	mov     ah, al
	and     ax, 0F00Fh
	test    dl, 0Fh
	jne     short efxvibratof0
	or      dl, al
efxvibratof0:
	test    dl, 0F0h
	jne     short efxvibratof1
	or      dl, ah
efxvibratof1:
	mov     [si+track.vibparm], dl
	mov     [si+track.effect], dx
	retn
efxvibrato2:
	mov     dh, dl
	and     dx, 0F00Fh
	shr     dh, 2
	mov     al, [si+track.vibpos]
	add     [si+track.vibpos], dh
	mov     dh, al
	shr     al, 2
	and     eax, 1Fh
	mov     al, [eax+sintable]
	mul     dl
	shr     ax, 7
	test    dh, dh
	jge     short efxvibratof2
	neg     ax
efxvibratof2:
	add     ax, [si+track.period]
	cmp     ax, 28
	jge     short efxvibratof3
	mov     ax, 28
efxvibratof3:
	cmp     ax, 3424
	jle     short efxvibratof4
	mov     ax, 3424
efxvibratof4:
	movzx   eax, ax
	mov     eax, [4*eax+pitchtable]
	mov     [4*ebx+voicepitch], eax
	retn

;-----------------------------------------------------------------------------
; efxtoneslide - volume slide and continue last portamento
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxtoneslide:
	call    efxvolslide
	mov     dl, [si+track.tonespeed]
	jmp     efxtoneporta

;-----------------------------------------------------------------------------
; efxvibslide - volume slide and continue last pitch vibrato
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxvibslide:
	call    efxvolslide
	mov     dl, [si+track.vibparm]
	jmp     short efxvibrato2

;-----------------------------------------------------------------------------
; efxtremolo - volume vibrato
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxtremolo:
	mov     al, [si+track.tremparm]
	mov     ah, al
	and     ax, 0F00Fh
	test    dl, 0Fh
	jne     short efxtremolof0
	or      dl, al
efxtremolof0:
	test    dl, 0F0h
	jne     short efxtremolof1
	or      dl, ah
efxtremolof1:
	mov     [si+track.tremparm], dl
	mov     [si+track.effect], dx
	retn
efxtremolo2:
	mov     dh, dl
	and     dx, 0F00Fh
	shr     dh, 2
	mov     al, [si+track.trempos]
	add     [si+track.trempos], dh
	mov     dh, al
	shr     al, 2
	and     eax, 1Fh
	mov     al, [eax+sintable]
	mul     dl
	shr     ax, 6
	test    dh, dh
	jge     short efxtremolof2
	neg     ax
efxtremolof2:
	add     al, [si+track.volume]
	jge     short efxtremolof3
	xor     al, al
efxtremolof3:
	cmp     al, 40h
	jle     short efxtremolof4
	mov     al, 40h
efxtremolof4:
	mul     byte [musicvolume]
	mov     [4*ebx+voicevolume], ah
	retn

;-----------------------------------------------------------------------------
; efxsampoffset - set the sample offset
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxsampoffset:
	movzx   eax, byte [si+track.inst]
	xor	esi, esi
	;lgs	si, [moduleptr]
	mov	si, [moduleptr]
	;mov	eax, [gs:esi+4*eax+module.sampptr]
	mov	eax, [esi+4*eax+module.sampptr]
	mov	dh, dl
	xor	dl, dl
	add     ax, dx
	mov     [4*ebx+voicepos], ax
	retn

;-----------------------------------------------------------------------------
; efxvolslide - volume slide
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxvolslide:
	mov     al, [si+track.volume]
	mov     dh, dl
	shr     dl, 4
	je      short efxvolslidef0
	add     al, dl
	cmp     al, 40h
	jle     short efxvolslidef1
	mov     al, 40h
efxvolslidef1:
	mov     [si+track.volume], al
	mul     byte [musicvolume]
	mov     [4*ebx+voicevolume], ah
	retn
efxvolslidef0:
	sub     al, dh
	jge     short efxvolslidef2
	xor     al, al
efxvolslidef2:
	mov     [si+track.volume], al
	mul     byte [musicvolume]
	mov     [4*ebx+voicevolume], ah
	retn

;-----------------------------------------------------------------------------
; efxpattjump - jump to order pattern
; In:
;  EBX = voice number
;  (DS:SI = track address)
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxpattjump:
	mov     [orderpos], dl
	mov     byte [pattrow], 40h
	retn

;-----------------------------------------------------------------------------
; efxsetvolume - set volume
; In:
;  EBX = voice number
;  DS:SI = track address
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxsetvolume:
	mov     al, dl
	mov     [si+track.volume], al
	mul     byte [musicvolume]
	mov     [4*ebx+voicevolume], ah
	retn

;-----------------------------------------------------------------------------
; efxbreak - break pattern
; In:
;  EBX = voice number
;  (DS:SI = track address)
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxbreak:
	mov     byte [pattrow], 40h
	retn

;-----------------------------------------------------------------------------
; efxsetspeed - set the tempo or BPM speed
; In:
;  EBX = voice number
;  (DS:SI = track address)
;  DL = effect parameter
;-----------------------------------------------------------------------------
efxsetspeed:
	test    dl, dl
	je      efxnull
	cmp     dl, 20h
	jae     short efxsetbpm
	mov     [tempo], dl
	mov     [tempocount], dl
	ret
efxsetbpm:
	mov     [bpm], dl
	call    mixstarttimer
	retn

;=============================================================================
; Sound Blaster Driver highlevel stuff
;=============================================================================

;-----------------------------------------------------------------------------
; mixinit - initialize the sound driver
; In:
;  AX = mixing speed in hertz
;  BL = number of voices
;  DX = I/O port address
;  CL = IRQ level
;  CH = DRQ channel
;  BH = polling mode
; Out:
;  CF = status
;-----------------------------------------------------------------------------
mixinit:
	; 29/09/2017 (MSDOS Modifications)

	pushad
	;push	es

	cmp	byte [playing], 0
	stc
	jne	mixinitd0

; setup sound card parameters

	mov     [manualmode], bh
	xor     bh, bh
	mov     [mixfreq], ax
	mov     [numvoices], bx
	mov     [ioaddr], dx
	mov     [irqnum], cl
	mov     [drqnum], ch

; check if the sound card is present

	call    sbreset
	jc      mixinitd0

; setup timer and double buffer variables

	mov     word [timerproc], nulltimer
	xor     ax, ax
	mov     [bufoff], ax
	mov     [timeracc], ax
	mov     word [timerspeed], 256

; clear voice parameters

	;push	es
	;mov	ax, ds
	;mov	es, ax
	;cld
	;lea	di, [voicepos]
	mov	di, voicepos
	mov     cx, 6*MAXVOICES
	xor     eax, eax
	rep     stosd
	;pop	es

; allocate conventional memory for the DMA buffer, the volume table,
; the mixing buffer and the boosting table.

	;mov	ax, 0100h
	;mov	bx, (DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15)/16
	;int	31h
	;jc	mixinitd0
	;mov	[bufsel], dx
	
	;movzx	eax, ax
	;shl	eax, 4

; Allocate memory for DMA buffer, Volume Table, Mixing Buffer
; and Boosting Table 
	; 29/09/2017
	;xor	eax, eax
	mov	ax, cs
	add	ax, 1000h
	shl	eax, 4
	add	eax, bss_end
	xor	ax, ax	
	mov	[dma_buffer], eax 
	;shr	eax, 4
	;mov	[bufsel], ax
	;shl	eax, 4

; set the address of the mixing buffer and boosting table

        mov     word [mixbuffer], 0
        mov     word [boosttable], DMABUFLEN
        add     eax, DMABUFLEN+2048

; get the address of the DMA buffer and volume table

        mov     ecx, DMABUFLEN
        lea     edx, [eax+ecx]

; check for cross-pages in the DMA buffer and align the Volume table

        mov     esi, eax
        add     si, cx
        jnc     short mixinitf0
        mov     edx, eax
        add     eax, VOLBUFLEN
mixinitf0:
        ;movzx	esi, [bufsel]
        ;shl	esi, 4
	mov	esi, [dma_buffer]
        sub     eax, esi
        sub     edx, esi
        add     dx, 255
        xor     dl, dl
        mov     [bufptr], ax
        mov     [voltable], dx

; clear DMA buffer with centered samples

        push    es
        cld
       ;mov     es, [bufsel]
       ; esi  = [dma_buffer]
	shr	esi, 4
	mov	es, si ; dma buffer segment
	mov     di, [bufptr]
        mov     cx, DMABUFLEN
        mov     al, 80h
        rep     stosb
        ;pop	es

; build volume table and boosting table

        ;push	es
        mov     cl, 6
        ;mov    es, [bufsel]
        ;mov	es, si ; dma buffer segment
	mov     di, [voltable]
        xor     bx, bx
mixinitl0:
        mov     al, bl
        imul    bh
        sar     ax, cl
        ;mov	[es:di], al
        ;inc	di
	stosb
        inc     bl
        jnz     short mixinitl0
        inc     bh
        cmp     bh, 40h
        jbe     short mixinitl0
        ;pop    es

        ;push   es
        ;cld
	;mov    es, [bufsel]
        ;mov	es, si ; dma buffer segment
        mov     di, [boosttable]
        mov     cx, 768
        xor     ax, ax
        rep     stosb
        mov     cx, 512
mixinitl1:
        mov     [es:di], ah
        add     ax, 80h
        inc     di
        loop    mixinitl1
        mov     cx, 768
        dec     al
        rep     stosb

        pop     es

push bx
push es
mov bx, 0B800h
mov es, bx
xor bx, bx
mov byte [es:bx], "#"
mov byte [es:bx+1], 4Eh
pop es
pop bx

; initialize the sound card for output

	call	dmasetup
	call	irqsetup
	call	sbsetup

push bx
push es
mov bx, 0B800h
mov es, bx
xor bx, bx
mov byte [es:bx+6], "?"
mov byte [es:bx+7], 4Eh
pop es
pop bx

; dont use the timer interrupt for manual polling mode

	cmp     byte [manualmode], 0
	jne     short mixinitf2

; install timer interrupt to poll the driver

	push	es
	mov	ax, cs
	mov	es, ax
	
	;lea	bx, [mixtimer]
	mov	bx, mixtimer
	mov	cl, 0
	call	irqsetvect
	mov	[oldtimeroff], bx
	mov	[oldtimersel], es
	pop	es
	
; set the timer frequency to 70 hertz

	cli
	mov	al, 36h
	out	43h, al
	mov	ax, TIMERRATE
	out	40h, al
	mov	al, ah
	out	40h, al
	sti

; set driver playing status

mixinitf2:
	mov     byte [playing], 1
	clc

mixinitd0:
	;pop	es
	popad
	retn

;-----------------------------------------------------------------------------
; mixdone - deinitialize the sound driver
;-----------------------------------------------------------------------------
mixdone:
	; 29/09/2017 (MSDOS Modifications)

	;pushad
	;push	es

	cmp     byte [playing], 1
	jne     short mixdoned0

; the timer interrupt was modified if we are using the timer polling mode

	cmp     byte [manualmode], 0
	jne     short mixdonef0

; restore the timer frequency to 18.2 hertz

	cli
	mov	al, 36h
	out	43h, al
	xor	al, al
	out	40h, al
	out	40h, al
	sti

; deinstall timer interrupt used to poll the driver

	push    es
	mov     bx, [oldtimeroff]
	mov     es, [oldtimersel]
	mov     cl, 0
	call    irqsetvect
	pop     es

; deinitialize the sound card output

mixdonef0:
	call    sbdone
	call    irqdone
	call    dmadone

; set driver stopped status

	mov	byte [playing], 0

mixdoned0:
	;pop	es
	;popad
	retn

;-----------------------------------------------------------------------------
; mixtimer - timer interrupt routine used to poll the sound driver
;-----------------------------------------------------------------------------
mixtimer:
	push    ax
	push    ds
push es	
	mov     ax, cs
	mov	ds, ax
mov es, ax

	call    mixpoll		 ; poll the sound system

	add	word [oldtimeracc], TIMERRATE
	jnc	short mixtimerf0  ; time to call the old IRQ0 vector?

push es
push bx
push ax
mov bx, 0B800h
mov es, bx
mov bx, 02h
mov al, [es:bx]
inc al
mov ah, 3Eh
mov [es:bx], ax
pop ax
pop bx
pop es
;	pushf			  ; yes, jump to the old IRQ0 service
;	call	dword [oldtimeroff]
;	jmp     short mixtimerd0
mixtimerf0:
	mov     al, 20h		  ; nope, send PIC acknowledge and exit
	out     20h, al
mixtimerd0:
pop es
	pop     ds
	pop     ax
	iret

;-----------------------------------------------------------------------------
; mixdetect - detect the sound card configuration
; Out:
;  DX = I/O Port
;  CL = IRQ level
;  CH = DMA channel
;  CF = status
;-----------------------------------------------------------------------------
mixdetect:
	cmp     byte [playing], 1    ; do not try to autodetect
	stc			     ; if we are already playing
	je      short mixdetectd0
	call    sbdetect
mixdetectd0:
	retn

;-----------------------------------------------------------------------------
; mixsettimerproc - set the timer procedure
; In:
;  DX = timer routine address
;-----------------------------------------------------------------------------
mixsettimerproc:
	mov     [timerproc], dx   ; set the timer callback address
	retn

;-----------------------------------------------------------------------------
; mixstarttimer - start the timer at the specified speed
; In:
;  DL = timer speed in beats per minute (BPMs)
;-----------------------------------------------------------------------------
mixstarttimer:
	push    ax
	push    bx
	push    dx
	mov     bh, dl		   ; set the timer callback speed
	xor     bl, bl		   ; to 24/60*BPM hertz
	mov     ax, [mixfreq]
	mov     dx, 0280h
	mul     dx
	div     bx
	mov     [timerspeed], ax
	pop     dx
	pop     bx
	pop     ax
	retn

;-----------------------------------------------------------------------------
; mixstoptimer - stop the timer routine
;-----------------------------------------------------------------------------
mixstoptimer:
	mov     word [timerproc], nulltimer
nulltimer:
	retn

;-----------------------------------------------------------------------------
; mixvoice - mixes the voice samples
; In:
;  EBX = voice number (*4)
;  CX = number of samples
;  ES:DI = buffer address
; Out:
;  ES:DI = buffer end address
;-----------------------------------------------------------------------------
mixvoice:
	; 30/09/2017
	; 29/09/2017 (MSDOS Modifications)

	push    eax
	;push	bx
	push    cx
	push    dx
	push    bp
	push    si

	push	ds
	push	es
	push	bx ; *

	mov	[_bx], bx ; 29/09/2017 !*!

	mov	ax, [voltable]
	mov     dl, [bx+3+voicefrac]
	mov     dh, [bx+1+voicepitch]
	mov	bp, [bx+2+voicepitch]
	add	ah, [bx+voicevolume]
	les	si, [bx+voicepos]
	mov	bx, ax
	mov	eax, [dma_buffer]
	shr	eax, 4
	mov	ds, ax

	movzx   eax, cl
	and     al, 31
	shr     cx, 5 ; / 32
	movzx   edi, di
	lea     edi, [edi+2*eax-2*32]
	
	;cmp	word [sp], 0 ; bx ; *
	cmp	word [cs:_bx], 0 ; bx ; !*!
	ja	short mixvoice_next
jmp CODESTART_33
	; bx  = 0 -> mov [edi+I], eax
	jmp     word [cs:2*eax+mixjmptable0] ; 30/09/2017
mixvoice_next:
jmp CODESTART_33
	; bx  > 0 -> add [edi+I], eax
	jmp	word [cs:2*eax+mixjmptable1] ; 30/09/2017

mixvoicef0:
	align   2
mixcodel0:
CODESTART0_0:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+0], ax
CODESTART0_1:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+2], ax
CODESTART0_2:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+4], ax
CODESTART0_3:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+6], ax
CODESTART0_4:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+8], ax
CODESTART0_5:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+10], ax
CODESTART0_6:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+12], ax
CODESTART0_7:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+14], ax
CODESTART0_8:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+16], ax
CODESTART0_9:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+18], ax
CODESTART0_10:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+20], ax
CODESTART0_11:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+22], ax
CODESTART0_12:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+24], ax
CODESTART0_13:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+26], ax
CODESTART0_14:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+28], ax
CODESTART0_15:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+30], ax
CODESTART0_16:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+32], ax
CODESTART0_17:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+34], ax
CODESTART0_18:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+36], ax
CODESTART0_19:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+38], ax
CODESTART0_20:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+40], ax
CODESTART0_21:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+42], ax
CODESTART0_22:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+44], ax
CODESTART0_23:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+46], ax
CODESTART0_24:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+48], ax
CODESTART0_25:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+50], ax
CODESTART0_26:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+52], ax
CODESTART0_27:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+54], ax
CODESTART0_28:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+56], ax
CODESTART0_29:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+58], ax
CODESTART0_30:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+60], ax
CODESTART0_31:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	mov	[ds:di+62], ax
CODESTART0_32:
	add     di, 2*32
	dec     cx 
	jge     mixcodel0

	jmp	mixvoicef2

mixvoicef1:
	align   2
mixcodel1:
CODESTART1_0:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+0], ax
CODESTART1_1:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+2], ax
CODESTART1_2:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+4], ax
CODESTART1_3:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+6], ax
CODESTART1_4:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+8], ax
CODESTART1_5:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+10], ax
CODESTART1_6:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+12], ax
CODESTART1_7:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+14], ax
CODESTART1_8:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+16], ax
CODESTART1_9:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+18], ax
CODESTART1_10:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+20], ax
CODESTART1_11:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+22], ax
CODESTART1_12:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+24], ax
CODESTART1_13:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+26], ax
CODESTART1_14:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+28], ax
CODESTART1_15:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+30], ax
CODESTART1_16:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+32], ax
CODESTART1_17:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+34], ax
CODESTART1_18:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+36], ax
CODESTART1_19:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+38], ax
CODESTART1_20:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+40], ax
CODESTART1_21:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+42], ax
CODESTART1_22:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+44], ax
CODESTART1_23:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+46], ax
CODESTART1_24:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+48], ax
CODESTART1_25:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+50], ax
CODESTART1_26:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+52], ax
CODESTART1_27:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+54], ax
CODESTART1_28:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+56], ax
CODESTART1_29:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+58], ax
CODESTART1_30:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+60], ax
CODESTART1_31:
	mov     bl, [es:si]
	add     dl, dh
	movsx   ax, byte [ds:bx]
	adc     si, bp
	add	[ds:di+62], ax
CODESTART1_32:
	add     di, 2*32
	dec     cx
	jge     mixcodel1

mixvoicef2:
	pop	bx ; *
	pop	es
	pop	ds
	mov     [bx+voicepos], si
	mov     [bx+3+voicefrac], dl

	pop     si
	pop     bp
	pop     dx
	pop     cx
	;pop	bx ; !*
	pop     eax
	retn

CODESTART_33:
	add 	al, '0'
	mov	ah, 4Ch
	mov	bx, 0B800h
	mov	es, bx
	xor	bx, bx
	mov	[es:bx], ax 
	jmp	short mixvoicef2

	align   4
mixjmptable0:
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33	

	dw	CODESTART0_32	
	dw	CODESTART0_31
	dw	CODESTART0_30
	dw	CODESTART0_29
	dw	CODESTART0_28	
	dw	CODESTART0_27
	dw	CODESTART0_26
	dw	CODESTART0_25
	dw	CODESTART0_24	
	dw	CODESTART0_23
	dw	CODESTART0_22
	dw	CODESTART0_21
	dw	CODESTART0_20	
	dw	CODESTART0_19
	dw	CODESTART0_18
	dw	CODESTART0_17
	dw	CODESTART0_16	
	dw	CODESTART0_15
	dw	CODESTART0_14
	dw	CODESTART0_13
	dw	CODESTART0_12	
	dw	CODESTART0_11
	dw	CODESTART0_10
	dw	CODESTART0_9
	dw	CODESTART0_8	
	dw	CODESTART0_7
	dw	CODESTART0_6
	dw	CODESTART0_5
	dw	CODESTART0_4	
	dw	CODESTART0_3
	dw	CODESTART0_2
	dw	CODESTART0_1
	dw	CODESTART0_0  ; not necessary here...

	;align   4
mixjmptable1:
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33
	dw 	CODESTART_33	

	dw	CODESTART1_32	
	dw	CODESTART1_31
	dw	CODESTART1_30
	dw	CODESTART1_29
	dw	CODESTART1_28	
	dw	CODESTART1_27
	dw	CODESTART1_26
	dw	CODESTART1_25
	dw	CODESTART1_24	
	dw	CODESTART1_23
	dw	CODESTART1_22
	dw	CODESTART1_21
	dw	CODESTART1_20	
	dw	CODESTART1_19
	dw	CODESTART1_18
	dw	CODESTART1_17
	dw	CODESTART1_16	
	dw	CODESTART1_15
	dw	CODESTART1_14
	dw	CODESTART1_13
	dw	CODESTART1_12	
	dw	CODESTART1_11
	dw	CODESTART1_10
	dw	CODESTART1_9
	dw	CODESTART1_8	
	dw	CODESTART1_7
	dw	CODESTART1_6
	dw	CODESTART1_5
	dw	CODESTART1_4	
	dw	CODESTART1_3
	dw	CODESTART1_2
	dw	CODESTART1_1
	dw	CODESTART1_0 ; not necessary here...

_bx:	dw	0	; 29/09/2017 !*!

;       macro   mixcode OPCODE
;       local   mixcodel0,mixjmptable
;
;       push    eax
;       push    bx
;       push    cx
;       push    dx
;       push    bp
;       push    si
;
;       push    ds
;       push    es
;       push    bx
;       mov     ax,[voltable]
;       mov     dl,[byte bx+3+voicefrac]
;       mov     dh,[byte bx+1+voicepitch]
;       mov     bp,[word bx+2+voicepitch]
;       add     ah,[byte bx+voicevolume]
;       les     si,[bx+voicepos]
;       mov     ds,[bufsel]
;       mov     bx,ax
;
;       movzx   eax,cl
;       and     al,31
;       shr     cx,5
;       movzx   edi,di
;       lea     edi,[edi+2*eax-2*32]
;       jmp     [2*eax+mixjmptable]
;
;       align   2
;mixcodel0:
;       I=0
;       rept    32
;CODESTART=$
;       mov     bl,[es:si]
;       add     dl,dh
;       movsx   ax,[byte ds:bx]
;       adc     si,bp
;       OPCODE  [ds:di+I],ax
;CODELEN=$-CODESTART
;       I=I+2
;       endm
;       add     di,2*32
;       dec     cx
;       jge     mixcodel0
;
;       pop     bx
;       pop     es
;       pop     ds
;       mov     [word low bx+voicepos],si
;       mov     [byte bx+3+voicefrac],dl
;
;       pop     si
;       pop     bp
;       pop     dx
;       pop     cx
;       pop     bx
;       pop     eax
;       ret
;
;       align   2
;       label mixjmptable word
;       I=CODESTART+CODELEN
;       rept    32
;       dw      I
;       I=I-CODELEN
;       endm
;       endm
;
;       test    bx,bx
;       je      mixvoicef0
;       mixcode add
;mixvoicef0:
;       mixcode mov

;-----------------------------------------------------------------------------
; mixvoices - mixes all the voices
; In:
;  ES:DI = buffer address
;  CX = number of samples
;-----------------------------------------------------------------------------
mixvoices:
	; 29/09/2017 (MSDOS Modifications)

	pushad

	xor     ebx, ebx
mixvoicesl0:
	push    bx
	push    cx
	push    di
	shl     bx, 2
	mov     bp, cx

mixvoicesl1:
	mov     cx, bp
	mov     dx, [bx+voicepos]
	mov     ax, [bx+voiceend]
	cmp     dx, ax
	jb      short mixvoicesf0
	sub     dx, ax
	add     dx, [bx+voiceloop]
	cmp     dx, ax
	jae     short mixvoicesc0
	mov     [bx+voicepos], dx

mixvoicesf0:
	sub     ax, dx
	shl     eax, 16
	mov     edx, [bx+voicefrac]
	shr     edx, 16
	sub     eax, edx

	movzx	edx, cx
	mov     esi, [bx+voicepitch]
	imul    edx, esi
	cmp     edx, eax
	jbe     short mixvoicesf1
	dec     eax
	xor     edx, edx
	add     eax, esi
	adc     edx, edx
	div     esi
	mov     cx, ax

mixvoicesf1:
push ax
push bx
push es
mov bx, 0B800h
mov es, bx
xor bx, bx
mov al, "H"
mov ah, 4Eh
mov [es:bx+10], ax
pop es
pop bx
pop ax
	call    mixvoice
	sub     bp, cx
	jg      short mixvoicesl1

	pop     di
	pop     cx
	pop     bx
	inc     bx
	cmp     bx, [numvoices]
	jb      short mixvoicesl0

	popad
	retn

mixvoicesc0:
	test    bx, bx
	jnz     short mixvoicesc1
	cld
	xor	ax, ax
	rep     stosw

mixvoicesc1:
	pop     di
	pop     cx
	pop     bx
	inc     bx
	cmp     bx, [numvoices]
	jb      mixvoicesl0

	popad
	retn

;-----------------------------------------------------------------------------
; mixpoll - updates the output buffer
;-----------------------------------------------------------------------------
mixpoll:
	; 29/09/2017 (MSDOS Modifications)

	pushad
	push	es

; get the 32 bit mixing buffer address

	mov     cx, DMABUFLEN/2
	mov	edi, [dma_buffer]
	shr	edi, 4
	mov	es, di
	;movzx	edi, word [mixbuffer]
	mov	di, [mixbuffer]

; check if we can fill the current half buffer with samples

	call    dmagetpos
	cmp     ax, cx
	jae     short mixpollf3
	cmp     [bufoff], cx
	je      short mixpollf4
	jmp     mixpolld0
mixpollf3:
	cmp     [bufoff], cx
	je      mixpolld0

; fill the mixing buffer and polls the timer callback routine

mixpollf4:
	mov     ax, [timeracc]
	mov     bp, cx
mixpolll0:
	test    ax, ax
	jg      short mixpollf0
	call    [timerproc]
	add     ax, [timerspeed]
mixpollf0:
	mov     cx, ax
	add     cx, 63
	;and	cl, not 63
	and	cl, ~63
	cmp     cx, bp
	jle     short mixpollf1
	mov     cx, bp
mixpollf1:
	call    mixvoices
	add	di, cx
	add	di, cx
	sub     ax, cx
	sub     bp, cx
	jg      short mixpolll0
	mov     [timeracc], ax

; translate 16-bit signed samples to 8-bit unsigned samples

	push	ds
	mov     cx, DMABUFLEN/2
	mov	si, [mixbuffer]
	movzx	eax, word [bufptr]
	add     ax, [bufoff]
	xor     [bufoff], cx
	mov     di, [boosttable]
	add     di, 1024
	shr     cx, 4
	mov	edx, [dma_buffer]
	shr	edx, 4
	mov	ds, dx
mixpolll2:
	%assign	I 0
	%rep	4
	mov     bx, [si+8*I+4]
	mov     dl, [di+bx]
	mov     bx, [si+8*I+6]
	mov     dh, [di+bx]
	shl     edx, 16
	mov     bx, [si+8*I]
	mov     dl, [di+bx]
	mov     bx, [si+8*I+2]
	mov     dh, [di+bx]
	mov     [eax+4*I], edx
	%assign	I I+1
	%endrep
	add     si, 2*16
	add     ax, 16
	dec     cx
	jg      short mixpolll2
	pop	ds

;       I=0
;       rept    4
;       mov     bx,[si+8*I+4]
;       mov     dl,[di+bx]
;       mov     bx,[si+8*I+6]
;       mov     dh,[di+bx]
;       shl     edx,16
;       mov     bx,[si+8*I]
;       mov     dl,[di+bx]
;       mov     bx,[si+8*I+2]
;       mov     dh,[di+bx]
;       mov     [eax+4*I],edx
;       I=I+1
;       endm
;       add     si,2*16
;       add     ax,16
;       dec     cx
;       jg      mixpolll2
;       pop     ds

mixpolld0:
	pop	es
	popad
	retn

;=============================================================================
; Sound Blaster DSP lowlevel stuff
;=============================================================================

;-----------------------------------------------------------------------------
; sbwrite - send a command/data byte to the DSP chip
; In:
;  AL = command/data byte
;-----------------------------------------------------------------------------
sbwrite:
	push    cx
	push    dx
	push    ax
	mov     dx, [ioaddr]
	add     dx, 0Ch
	mov	ah, al
	xor	cx, cx		; wait until the write buffer
sbwritel0:			; status port (2XCh) bit 7 is clear
	in	al, dx
	and     al, 80h
	loopnz  sbwritel0

	mov	al, ah		   ; write value in the write
	out	dx, al		   ; data port (2XCh)
	pop     ax
	pop     dx
	pop	cx
	retn

;-----------------------------------------------------------------------------
; sbreset - reset the Sound Blaster DSP chip
; Out:
;  CF = status
;-----------------------------------------------------------------------------
sbreset:
	push    ax
	push    bx
	push    cx
	push    dx
	mov	bx, 64			; try to reset upto 64 times
sbresetl1:
	mov     dx, [ioaddr]
	add     dx, 06h
	mov     al, 1			; write 1 to the reset port (2X6h)
	out	dx, al
	xor	ah, ah			; wait at least 3 microseconds
sbresetl2:
	in	al, dx
	dec	ah 
	jnz	short sbresetl2
	xor     al, al			; write 0 to the reset port (2X6h)
	out	dx, al
	add     dx, 08h
	mov	cx, 0400h		; wait until the data available
sbresetl0:			      ; status port (2XEh) bit 7 is set
	in	al,  dx
	and     al, 80h
	loopz   sbresetl0
	sub     dx, 04h			; read the read data port (2XAh)
	in	al, dx
	cmp     al, 0AAh
	clc
	je      short sbresetd0		; check the ready byte value
	dec     bx
	jnz     short sbresetl1
	stc
sbresetd0:
	pop     dx
	pop     cx
	pop     bx
	pop     ax
	retn

;-----------------------------------------------------------------------------
; sbsetup - start the DMA output
;-----------------------------------------------------------------------------
sbsetup:
	push    ax
	push    dx
	mov     al, 0D1h	; turn on the speaker
	call    sbwrite

	;mov    al, 40h		; set the playback rate
	;call	sbwrite
	;mov    ax, 1000
	;mul    ax
	;div    word [mixfreq]
	;neg    ax
	;call	sbwrite

	mov	al, 41h
	call	sbwrite

	mov	ax, [mixfreq]
	xchg	ah, al
	push	ax
	call    sbwrite
	pop	ax
	mov	al, ah
	call	sbwrite
	;
	;mov	al, 14h	; start the lowspeed 8 bit DMA
	;call	sbwrite	; mode transfer to the DAC
	mov	al, 0C6h
	call	sbwrite
	mov	al, 10h
	call	sbwrite
	
	mov     al, 0FFh
	call    sbwrite
	call    sbwrite
	pop     dx
	pop     ax
	retn

;-----------------------------------------------------------------------------
; sbdone - shut down the DMA output
;-----------------------------------------------------------------------------
sbdone:
	push    ax
	call    sbreset		; reset the DSP chip
	mov     al, 0D3h
	call    sbwrite		; turn off the speaker
	pop     ax
	retn

;-----------------------------------------------------------------------------
; sbdetect - Detect the Sound Blaster I/O Port, IRQ level and DMA channel
; Out:
;  DX = I/O port address
;  CL = IRQ level
;  CH = DMA channel
;  CF = status
;-----------------------------------------------------------------------------
sbdetect:
	mov     dx, 210h		; scan the ports 210h..260h
sbdetectl0:
	mov     [ioaddr], dx		; check if there is a SB card
	call    sbreset			; trying to reset the DSP chip
	jnc     sbdetectf0
	add     dx, 10h
	cmp     dx, 260h
	jbe     sbdetectl0
	xor     dx, dx
	xor     cx, cx
	stc
	retn

sbdetectf0:
	push	ax
	push	bx
	push	cx
	push	es

ScanIRQ:
SetIrqs:
	push	cs
	pop	es
	mov	bx, irqtest_2
	mov	cx, 2
	call	irqsetvect
	call	irqsetmask
        push    bx ; *
        push    es ; *
	push	cs
	pop	es
	mov	bx, irqtest_3
	mov	cx, 3
	call	irqsetvect
	call	irqsetmask
        push    bx ; **
        push    es ; **
	push	cs
	pop	es
	mov	bx, irqtest_5
	mov	cx, 5
	call	irqsetvect
	call	irqsetmask
        push    bx ; ***
        push    es ; ***
	push	cs
	pop	es
	mov	bx, irqtest_7
	mov	cx, 7
	call	irqsetvect
	call	irqsetmask
        push    bx ; ****
        push    es ; ****
	push	cs
	pop	es
	mov	bx, irqtest_10
	mov	cx, 10
	call	irqsetvect
	call	irqsetmask
        push    bx ; *****
        push    es ; *****
	push	cs
	pop	es

	mov     byte [irqnum], 0

	mov     al, 0F2h		; ask to the DSP to raise a IRQ
	call    sbwrite

	xor	cx, cx			; wait until some IRQ occurs
sbdetectl1:
	cmp     byte [irqnum], 0
	loope	sbdetectl1

RestoreIrqs:
	pop	es ; *****
        pop	bx ; *****
	mov	cx, 100h+10
	call	irqsetmask
	call	irqsetvect
	pop	es ; ****
        pop	bx ; ****
	mov	cx, 100h+7
	call	irqsetmask
	call	irqsetvect
	pop	es ; ***
        pop	bx ; ***
	mov	cx, 100h+5
	call	irqsetmask
	call	irqsetvect
	pop	es ; **
        pop	bx ; **
	mov	cx, 100h+3
	call	irqsetmask
	call	irqsetvect
	pop	es ; *
        pop	bx ; *
	mov	cx, 100h+2
	call	irqsetmask
	call	irqsetvect	

	pop	es
	pop	cx
	pop	bx
	pop	ax

	mov	dx, [ioaddr]		; return the SB parameters
	mov	cl, [irqnum]
	xor	ch, ch
	sub     ch, cl
	mov     ch, 1			; DMA Channel
	cmc
	retn

;=============================================================================
; Sound Blaster DMA lowlevel stuff
;=============================================================================

;-----------------------------------------------------------------------------
; dmasetup - setup the DMA buffer parameters
;-----------------------------------------------------------------------------
dmasetup:
	push    eax
	push	edx
	push    bx
	push    cx

	mov     bl, [drqnum]
	mov     al, bl
	or      al, 04h		; reset the DMA channel
	out	0Ah, al
	out	0Ch, al		; clear the flip flop
	mov     al, bl
	or      al, 58h		; set the autoinit mode
	out	0Bh, al
	movzx	dx, bl
	add     dx, dx
	mov	eax, [dma_buffer] ; set the buffer address
	out	dx, al
	mov	al, ah
	out	dx, al
	inc     dl
	mov	ax, DMABUFLEN	; set the buffer length
	dec	ax
	out	dx, al
	mov	al, ah
	out	dx, al
	mov     edx, 82818387h	; set the buffer page
	mov     cl, bl
	shl     cl, 3
	shr     edx, cl
	xor     dh, dh
	shr	eax, 16
	out	dx, al
	mov     al, bl		; unlock the DMA channel
	out	0Ah, al

	pop	cx
	pop	bx
	pop	edx
	pop     eax
	retn

;-----------------------------------------------------------------------------
; dmadone - shut down the DMA controller
;-----------------------------------------------------------------------------
dmadone:
	push	ax
	mov	al, [drqnum]	; reset the DMA channel
	or	al, 04h
	out	0Ah, al
	pop	ax
	retn

;-----------------------------------------------------------------------------
; dmagetpos - return the DMA buffer relative position
; Out:
;  AX = buffer relative position
;-----------------------------------------------------------------------------
dmagetpos:
	push    cx
	push    dx
	out	0Ch, al		; clear the flip flop
	mov     dl, [drqnum]
	xor     dh, dh
	add     dl, dl
	inc     dl
	in	al, dx		; read the DMA counter
	mov	ah, al
	in	al, dx
	xchg	al, ah
dmagetposl0:
	mov	cx, ax		; read again the DMA counter
	in	al, dx
	mov	ah, al
	in	al, dx
	xchg	al, ah
	sub     cx, ax
	cmp     cx, +16		; both values are near?
	jg      short dmagetposl0 ; nope, try again
	cmp     cx, -16
	jl      short dmagetposl0
	neg	ax		; get the position relative
	add	ax, DMABUFLEN	; to the start of the buffer
	pop     dx
	pop     cx
	retn

;=============================================================================
; Sound Blaster IRQ lowlevel stuff
;=============================================================================

;------------------------------------------------------------------------------
; irqsetvect - set the IRQ handler routine
; In:
;  ES:BX = IRQ handler routine address
;  CL = IRQ level
; Out:
;  ES:BX = previous IRQ handler address
;------------------------------------------------------------------------------
irqsetvect:
        push	ax
        push	cx
        push	dx

; get the PIC interrupt master and slave base address

        mov     dx, 0870h

; get the IDT interrupt slot number for the IRQ number

        mov	al, cl
        cmp	al, 08h
        jb	short irqsetvectf0
        mov	dh, dl
        sub	al, 08h
irqsetvectf0:
        add	al, dh

; saves and change the IRQ handler routine

        push	ds
        push	es
        push	bx
        mov	ah, 35h
        int	21h
        pop	dx
        pop	ds
        push	es
        push	bx
        mov	ah, 25h
        int	21h
        pop	bx
        pop	es
        pop	ds

        pop     dx
        pop     cx
        pop     ax
        retn

;------------------------------------------------------------------------------
; irqsetmask - enable or disable the IRQ in the interrupt mask registers
; In:
;  CL = IRQ level
;  CH = enable (=0) or disable (=1)
;------------------------------------------------------------------------------
irqsetmask:
        push    ax
        push    dx

        cli
        in      al, 0A1h		; enable or disable the specified
        mov     ah, al			; IRQ using the PIC interrupt
        in      al, 21h			; mask registers
        mov     dx, 1
        shl     dx, cl
        not     dx
        and     ax, dx
        mov     dl, ch
        shl     dx, cl
        or      ax, dx
        out     21h, al
        mov     al, ah
        out     0A1h, al
        sti

        pop     dx
        pop     ax
        retn	

;-----------------------------------------------------------------------------
; irqsetup - install the IRQ handler routine
;-----------------------------------------------------------------------------
irqsetup:
	push	ax
	push	bx
	push	cx

	push	es
	;mov	ax, cs
	;mov	es, ax

	;lea	bx, [irqhandler]
	mov	bx, irqhandler
	mov	cl, [irqnum]
	call	irqsetvect
        mov     [oldirqoff], bx
        mov     [oldirqsel], es
	pop	es	

	pop	cx
	pop	bx
	pop	ax
	retn

;-----------------------------------------------------------------------------
; irqdone - restores the old IRQ handler routine
;-----------------------------------------------------------------------------
irqdone:
        push	bx
        push	cx

; disable IRQ signals in the PIC interrupt mask register

        mov	cl, [irqnum]
        mov	ch, 1
        call	irqsetmask

; restore the old IRQ handler routine

        push	es
        mov	bx, [oldirqoff]
        mov	es, [oldirqsel]
        mov	cl, [irqnum]
        call	irqsetvect
        pop	es

        pop	cx
        pop	bx
        retn

;--------------------------------------------------------------------------
; irqhandler - hardware IRQ handler routine
;--------------------------------------------------------------------------
irqhandler:
        push    ax
        push    dx
        push    ds

        mov	ax, cs
        mov     ds, ax

; send acknowledge to the PIC controller

        mov     al, 20h
	cmp     byte [irqnum], 08h
	jb      short irqhandlerf0
	out     0A0h, al
irqhandlerf0:
        out     20h, al

; send acknowledge to the DSP chip reading the 8 bit ack port (2XEh)

        mov     dx, [ioaddr]
        add     dx, 0Eh
        in      al, dx

; restart the 8 bit DMA mode playback transfer

        mov     al, 14h
        call    sbwrite
        mov     al, 0FFh
        call    sbwrite
        call    sbwrite

        pop     ds
        pop     dx
        pop     ax
        iret

;--------------------------------------------------------------------------
; irqtest - testing hardware IRQ handler routine
;--------------------------------------------------------------------------
irqtest:
        push    dx			; common IRQ test handler code
        push    ds			; used for autodetection
        mov     dx, cs
        mov     ds, dx
        mov     [irqnum], al		; save the IRQ level number
        mov     dx, [ioaddr]		; send acknowledge signal to the
        add     dx, 0Eh			; DSP reading the ack port (2XEh)
        in      al, dx
        mov     al, 20h			; send acknowledge to the PIC
	cmp     byte [irqnum], 08h	; controllers
	jb      short irqtestf0
	out     0A0h, al
irqtestf0:
        out     20h, al
        pop     ds
        pop     dx
        pop     ax
        iret

irqtest_2:                              ; possible IRQ levels
        push    ax
        mov     ax, 2
        jmp     short irqtest
irqtest_3:
        push    ax
        mov     ax, 3
        jmp     short irqtest
irqtest_5:
        push    ax
        mov     ax, 5
        jmp     short irqtest
irqtest_7:
        push    ax
        mov     ax, 7
        jmp     short irqtest
irqtest_10:
	push    ax
	mov     ax, 10
	jmp     short irqtest

;*****************************************************************************
; modload.c (21/06/1994) in 'TNYPL211'
;*****************************************************************************
;		Tiny MOD Player for Watcom C/C++32 and DOS/4GW
;		   Module and Sample file loader routines.
;
;		      Copyright 1993,94 Carlos Hasan
;*****************************************************************************

;=============================================================================
; equates & structures
;=============================================================================

; /* MOD FileFormat */

ID_MK	equ 2E4B2E4Dh ; "M.K."
ID_FLT4 equ 34544C46h ; "FLT4"
ID_6CHN equ 4E484336h ; "6CHN"
ID_8CHN equ 4E484338h ; "8CHN"
;	
ID_FLT8 equ 38544C46h ; "FLT8" ; Erdogan Tan (SB16MOD.ASM)

struc MODSample	; typedef struct {
  .SampleName:	resb 22	; byte SampleName[22];
  .Length:	resw 1  ; word Length;
  .FineTune:	resb 1	; byte FineTune;
  .Volume:	resb 1	; byte Volume;
  .LoopStart:	resw 1	; word LoopStart;
  .LoopLength:	resw 1	; word LoopLength;
  .size:
endstruc	; } MODSample;

struc MODHeader	; typedef struct {
  .SongName:	resb 20	; byte SongName[20];
  .Samples:	resb 31*MODSample.size ; MODSample Samples[31];
  .OrderLength:	resb 1	; byte OrderLength;
  .Restart:	resb 1	; byte ReStart;
  .Orders:	resb 128 ; byte Orders[128];
  .Sign:	resd 1	; sword Sign;
  .size:
endstruc	; } MODHeader;

; /* WAV fileformat */

ID_RIFF equ 46464952h ; "RIFF"
ID_WAVE equ 45564157h ; "WAVE"
ID_FMT  equ 20746D66h ; "fmt "
ID_DATA equ 61746164h ; "data"

struc WAVHeader ; typedef struct {
  .RIFFMagic:	resd 1 ; dword RIFFMagic;
  .FileLength:	resd 1 ; dword FileLength;
  .FileType:	resd 1 ; dword FileType;
  .FormMagic:	resd 1 ; dword FormMagic;
  .FormLength:	resd 1 ; dword FormLength;
  .SampleFormat: resw 1 ; word SampleFormat;
  .NumChannels: resw 1 ; word NumChannels;
  .PlayRate:	resd 1 ; dword PlayRate;
  .BytesPerSec:	resd 1 ; dword BytesPerSec;
  .Pad:		resw 1 ; word Pad;
  .BitsPerSample: resw 1 ; word BitsPerSample;
  .DataMagic:	resd 1 ; dword DataMagic;
  .DataLength:	resd 1 ; dword DataLength;
  .size:
endstruc	; } WAVHeader;

;=============================================================================
; mod loader code
;=============================================================================

; /* MOD Loader Routine */

MODLoadModule:
;Module *MODLoadModule(char *Path)
OpenFile: 
  	; 29/09/2017 (MSDOS PSP modifications) 
	; es:di = file name address (in PSP area)   
	push	ds
	push	ds
	push	es
	pop	ds
	pop	es ; es = ds = cs
	mov	dx, di ; mod_file_name
	mov	ax, 3D00h  ; Open File	
	int	21h
	pop	ds
	jc	short gdlm0
	mov     [FileHandle], ax
ReadHeader:
	mov	bx, ax
	mov	dx, Header
	mov	cx, MODHeader.size
	mov	ax, 3F00h
	int	21h ; Read File
	jc	short gdlm_err	
	;jnc	short gdlm1

	cmp	ax, MODHeader.size
	je	short gdlm1
gdlm_err:
	call	CloseFile
gdlm0:
	xor	ax, ax  ; return NULL;
	retn
gdlm1:
	mov	eax, [Header+MODHeader.Sign]

	cmp	eax, ID_MK   ; cmp eax, ".K.M"
	je	short gdlm4
	cmp	eax, ID_FLT4 ; cmp eax, "4TLF"
	je	short gdlm4

	cmp	eax, ID_6CHN ; cmp eax,	"NHC6"
	je	short gdlm3

	cmp	eax, ID_8CHN ; cmp eax,	"NHC8"
	je	short gdlm2

	cmp	eax, ID_FLT8 ; cmp eax, "8TLF"
	jne	short gdlm_err
gdlm2:
	mov	ax, 8	; 8-CHANNEL-MOD	
	jmp	short gdlm5
gdlm3:
	mov	ax, 6	; 6-CHANNEL-MOD	
	jmp	short gdlm5
gdlm4:
	mov	ax, 4	; 4-CHANNEL-MOD
gdlm5:
	mov	[numtracks], ax	; Number of channels
	mov	[Song+module.numtracks], ax

	mov	al, [Header+MODHeader.OrderLength]
	mov	[Song+module.orderlen], ax ; ah = 0

	xor	edx, edx ; *!

	mov	si, Header+MODHeader.Orders
	mov	di, Song+module.orders
	mov	cx, 128
gdlm6:
	lodsb
	stosb
	cmp	dl, al    ; Max. Pattern Number
	jnb	short gdlm7
	mov	dl, al 	
gdlm7:
	loop	gdlm6
	inc	dl
	; dx = Number of patterns (<=128)
	mov	[num_patts], dl

	; 29/09/2017
	xor	eax, eax
	mov	ax, cs
	shl	eax, 4
	add	eax, 20000h ; cs + 128K
	mov	[pattern_buffer], eax

	mov	cl, dl
	mov	di, Song+module.patterns
	mov	dx, [numtracks] ; number of channels ; *!
	shl	dx, 8 ; (256*NumTracks)
	mov	si, di
gdlm8:
	stosd
	add	eax, edx ; *!
	loop	gdlm8

	mov	[sample_buffer], eax

	mov	cl, [num_patts]
	;mov	si, Song+module.patterns
	mov	bx, [FileHandle]
ReadPatterns:  
	push	ds
	push	cx  ; * ; patterns count down
	push	dx  ; byte count to read
	mov	cx, dx	
	xor	dx, dx
	lodsd
	;mov	edx, eax
	shr	eax, 4	
	mov	ds, ax	; Buffer segment
	;and	edx, 0Fh ; Offset = 0
	mov	ax, 3F00h ; Read file
	int	21h
	pop	dx
	pop	cx ; *
	pop	ds
	jc      gdlm_err
	loop	ReadPatterns

	mov	cl, [num_patts]
	mov	bx, Song+module.patterns
	
	; 29/09/2017 (MSDOS modifications)
	push	es ; !*!
gdlm9:
	push	ebx ; *
	push	cx ; **

	mov	eax, [bx]
	;mov	di, ax
	shr	eax, 4
	mov	es, ax  ; !*!
	;and	di, 0Fh	; Offset = 0 ; !!

	xor	di, di  ; !!

	mov	cx, [numtracks]
	shl	cx, 6 ; *64
gdlm10: 
	mov	eax, [es:di] ;  !*!, !!

	; EAX = Note (0 to 63)	

	;Info for each note:

	; _____byte 1_____   byte2_    _____byte 3_____   byte4_
	;/                \ /      \  /                \ /      \
	;0000          0000-00000000  0000          0000-00000000

	;Upper four    12 bits for    Lower four    Effect command.
	;bits of sam-  note period.   bits of sam-
	;ple number.                  ple number.

	;for (Patt = Song->Patterns[i],j=0; j<64*NumTracks; Patt += 4, j++) {
        ;   Period = ((word)(Patt[0] & 0x0F) << 8) | (Patt[1]);
        ;   Inst = (Patt[0] & 0xF0) | (Patt[2] >> 4);
        ;   Effect = ((word)(Patt[2] & 0x0F) << 8) | (Patt[3]);
	movzx	edx, al
	and	dl, 0Fh
	shl	dx, 8		
	mov	dl, ah
	; dx = Period  (12 bits)
	push	edx ; ****
	; Inst = (Patt[0] & 0xF0) | (Patt[2] >> 4);
	mov	dl, al
	and	dx, 0F0h
	shr	eax, 16		
	movzx	ebx, al
	shr	bl, 4
	or	dl, bl  ; Instrument/Sample number (8 bits)
	; Effect = ((word)(Patt[2] & 0x0F) << 8) | (Patt[3]);
	;mov	bl, al
	;and	bx, 0Fh
	;shl	bx, 8
	;mov	bl, ah
	mov	bh, al
	and	bh, 0Fh
	mov	bl, ah
	; bx = Effect (12 bits)
	pop	eax ; **** ; AX = Period
	; DL = Instrument
	; BX = Effect
	push	ebx ; ***
	xor	ebx, ebx ; 0 ; Note
gdlm11:	
	; for (Note = 0; Note < 12*7; Note++)
        ;       if (Period >= PeriodTable[Note]) break;
	cmp	ax, [periodtable+2+bx]
	jae	short gdlm12
	inc	bl
	inc	bl
	cmp	bl, 7*12*2 ; 84 note words 
	jb	short gdlm11
        ; if (Note == 12*7) Note = 0; else Note++;
	sub	bl, bl ; 0
	jmp	short gdlm13
gdlm12:
	shr	bl, 1
	inc	bl
gdlm13:	
	pop	eax ; *** ; Effect in AX
	shl	eax, 16
	;Patt[0] = Note;
        ;Patt[1] = Inst;
        ;Patt[2] = Effect & 0xFF;
        ;Patt[3] = Effect >> 8;
	mov	al, bl ; Note ; 0 to 84
	mov	ah, dl ; Instrument			
	
	;mov	[es:di], eax ; !*!
	;add	di, 4
	stosd	; !*!

	loop	gdlm10

	pop	cx ; **
	pop	ebx ; *

	add	bx, 4
	loop	gdlm9

	pop	es ; !*! ; 29/09/2017	

	; ***

	; MODHeader.Samples: resb 31*MODSample.size

	;for (i = 1; i <= 31; i++) {
        ;Sample = &Header.Samples[i-1];
        ;if (Sample->Length) {

	mov	cl, 31
	mov	si, Header+MODHeader.Samples
	mov	edi, [sample_buffer] ; 29/09/2017
gdlm14:
	mov	dx, [si+MODSample.Length]
	and	dx, dx
	jz	gdlm17

	mov	ax, [si+MODSample.LoopStart]
	xchg	ah, al ; word
	shl	ax, 1  ; byte
	mov	[LoopStart], ax

	mov	ax, [si+MODSample.LoopLength]
	xchg	ah, al ; word
	shl	ax, 1  ; byte
	mov	[LoopLength], ax
		
        ;Length = Swap(Sample->Length) << 1;
        ;LoopStart = Swap(Sample->LoopStart) << 1;
        ;LoopLength = Swap(Sample->LoopLength) << 1;

	xchg	dh, dl  ; word
	shl	dx, 1	; byte

	push	ds ; *
	push	cx ; **
	push	dx ; ***

	mov	bx, [FileHandle]  ; 30/09/2017
	mov	cx, dx ;  cx = byte count to read
	mov	eax, edi
	mov	dx, ax
	shr	eax, 4
	mov	ds, ax	; Sample segment
	and	dx, 0Fh ; Sample offset (= 0 !?)	
	mov	ax, 3F00h ; Read File
	int	21h

	pop	dx ; ***
	pop	cx ; **
	pop	ds ; *

	jc	gdlm_err

	;cmp	ax, dx
	;jne	short gdlm_err

	mov	bx, 32
	sub	bx, cx ; 1 to 31
	shl	bl, 2

	; 29/09/2017
	mov	eax, edi
	;shr	eax, 4
	;shl	eax, 16 ; segment
	shl	eax, 12
	mov	ax, di
	and	ax, 0Fh ; offset
	; *!*
	;mov	[Song+module.sampptr+bx], edi
	mov	[Song+module.sampptr+bx], eax ; segment:offset 
	;

	; edx = dx = Length 
	
	;if (LoopLength <= 2) {
        ;   Song->SampLoop[i] = Song->SampPtr[i] + Length;
        ;   Song->SampEnd[i] = Song->SampLoop[i];
        ;}
        ;else {
        ;   Song->SampLoop[i] = Song->SampPtr[i] + LoopStart;
        ;   Song->SampEnd[i] = Song->SampLoop[i] + LoopLength;
        ;}

	cmp	word [LoopLength], 2
	jnb	short gdlm15

	; 29/09/2017
	;mov	eax, edx
	;add	eax, edi ; add sample length to current sample offset
			 ; to point next sample offset
	; eax = Song->SampPtr[i] + Length;
	add	ax, dx ; sample offset + length ; 29/09/2017 ; *!*
	mov	[Song+module.samploop+bx], eax
	mov	[Song+module.sampend+bx], eax
	jmp	short gdlm16
gdlm15:
	;movzx	eax, word [LoopStart]
	;add	eax, edi ; Song->SampPtr[i] + LoopStart;
	add	ax, [LoopStart] ; 29/09/2017 ; *!*
	mov	[Song+module.samploop+bx], eax
	;push	dx
	;;movzx	edx, word [LoopLength]
	;mov	dx, [LoopLength]
	;add	eax, edx
	;pop	dx
	add	ax, [LoopLength] ; 29/09/2017 ; *!*
	;add	eax, edi ; Song->SampLoop[i] + LoopLength;
	mov	[Song+module.sampend+bx], eax
gdlm16:
        ; Song->SampVolume[i] = Sample->Volume;
	mov	al, [esi+MODSample.Volume]
	mov	[Song+module.sampvolume], al

	add	edi, edx ; edi = edi + Length
gdlm17:
	add	si, MODSample.size

	;loop	gdlm14
	dec	cl
	jnz	gdlm14	
	
	call	CloseFile
	mov	eax, Song  ; return Song;
	retn
	
CloseFile:      
	; 29/09/2017 ; (MSDOS modifications)
	mov	bx, [FileHandle]
	mov	ax, 3E00h ; Close file
	int	21h
	retn

MODFreeModule:
; void MODFreeModule(Module *Song)
; 	! Nothing to do in TRDOS386 !
	retn

;=============================================================================
; RIFF/WAV Loader Routine
;=============================================================================

MODLoadSample:
	; 30/09/2017
	; 29/09/2017 (MSDOS modification)
; Sample *MODLoadSample(char *Path)

	; ds:dx = ASCIIZ file name address
	mov	ax, 3D00h  ; open file for reading
 	int	21h
	jc	short gdlw0

	mov     [FileHandle], ax

ReadWavHeader:
	push	ds

	mov	bx, ax
	; bx = File handle
	mov	cx, WAVHeader.size
	; cx = number of bytes to read
	mov	dx, WaveInfo
	; ds:dx = Buffer address
	mov	ax, 3F00h  ; read file	
	int	21h
	jc	short gdlw_err	
	;jnc	short gdlw1

	cmp	ax, WAVHeader.size
	je	short gdlw1
gdlw_err:
	pop	ds
	call	CloseFile
gdlw0:
	xor	ax, ax  ; return NULL;
	retn
gdlw1:
	; "RIFF"
	cmp	dword [WaveInfo+WAVHeader.RIFFMagic], ID_RIFF
	jne	short gdlw_err
	; "WAVE"
	cmp	dword [WaveInfo+WAVHeader.FileType], ID_WAVE
	jne	short gdlw_err
	; "fmt "
	cmp	dword [WaveInfo+WAVHeader.FormMagic], ID_FMT
	jne	short gdlw_err
	; "data"
	cmp	dword [WaveInfo+WAVHeader.DataMagic], ID_DATA
	jne	short gdlw_err
	; 8 bit mono WAV file
	cmp	word [WaveInfo+WAVHeader.SampleFormat], 1
	jne	short gdlw_err
	cmp	word [WaveInfo+WAVHeader.NumChannels], 1
	jne	short gdlw_err
	cmp	word [WaveInfo+WAVHeader.BitsPerSample], 8
	jne	short gdlw_err

	mov	eax, 1024*1024  ; 1 MB
	mov	esi, eax
	add	eax, sample.size ; sample (header) structure size
	shr	eax, 4
	mov	ds, ax

	; Instr->Period = (8363L*428L)/Header.PlayRate;
    	; Instr->Volume = 64;
    	; Instr->Length = Header.DataLength;
    	; Instr->Data = (byte*)(Instr+1);

        ;	         7093789.2
	; SampleRate = -------------- (For a PAL machine)
        ;	         Period * 2

        ; 	         7159090.5
	; SampleRate = -------------- (For a NTSC machine)
        ;		 Period * 2

	;;; 8363*428*2 = 7158728

	xor	edx, edx
	mov	eax, 8363*428
	mov	ebx, [cs:WaveInfo+WAVHeader.PlayRate]
	div	ebx
	mov	[sample.period], ax 
	mov	word [sample.volume], 64 
	mov	ecx, [cs:WaveInfo+WAVHeader.DataLength]
	mov	[sample.datalen], ecx 	

	shr	esi, 4
	mov	ax, si ; segment
	mov	ds, ax
	and	si, 0Fh
	mov	dx, si
	shl	eax, 16
	mov	ax, dx ; offset
	mov	[sample.dataptr], eax ; segment:offset

	mov	bx, [cs:FileHandle]	
	; bx = File handle
	; cx = Byte count
	; ds:dx = Buffer address
	mov	eax, 3F00h ; Read file
	int	21h
	jc	gdlw_err	

	cmp	ax, cx ; WAVHeader.DataLength
	jne	gdlw_err
	
	; for (ptr = Instr->Data, len = Instr->Length; len; len--)
        ; *ptr++ ^= 0x80;

	; cx =  Wave data length in bytes
	; ds:si = Buffer address for wave data (= 1MB + sample.size)	
gdlw2:
	xor	byte [si], 80h
	inc	si		
	loop	gdlw2

	pop	ds
	call	CloseFile

	mov	ax, si  ; return Instr;
	retn
 
MODFreeSample:
; void MODFreeSample(Sample *Instr)
; 	! Nothing to do for COM file !
	retn

;=============================================================================
;		DATA
;=============================================================================

;=============================================================================
; Protracker effects stuff
;=============================================================================

;-----------------------------------------------------------------------------
; Effect jump tables
;-----------------------------------------------------------------------------

align 4

efxtable:
	dw      efxarpeggio	; 0 - arpeggio
	dw      efxnull		; 1 - porta up
	dw      efxnull		; 2 - porta down
	dw      efxtoneporta	; 3 - tone porta
	dw      efxvibrato	; 4 - vibrato
	dw      efxnull		; 5 - tone+slide
	dw      efxnull		; 6 - vibrato+slide
	dw      efxtremolo	; 7 - tremolo
	dw      efxnull		; 8 - unused
	dw      efxsampoffset	; 9 - sample offset
	dw      efxnull		; A - volume slide
	dw      efxpattjump	; B - pattern jump
	dw      efxsetvolume	; C - set volume
	dw      efxbreak	; D - break pattern
	dw      efxnull		; E - extra effects
	dw      efxsetspeed	; F - set speed

efxtable2:
	dw      efxarpeggio2	; 0 - arpeggio
	dw      efxportaup	; 1 - porta up
	dw      efxportadown	; 2 - porta down
	dw      efxtoneporta2	; 3 - tone porta
	dw      efxvibrato2	; 4 - vibrato
	dw      efxtoneslide	; 5 - tone+slide
	dw      efxvibslide	; 6 - vibrato+slide
	dw      efxtremolo2	; 7 - tremolo
	dw      efxnull		; 8 - unused
	dw      efxnull		; 9 - sample offset
	dw      efxvolslide	; A - volume slide
	dw      efxnull		; B - pattern jump
	dw      efxnull		; C - set volume
	dw      efxnull		; D - break pattern
	dw      efxnull		; E - extra effects
	dw      efxnull		; F - set speed

;-----------------------------------------------------------------------------
; Amiga period table
;-----------------------------------------------------------------------------

periodtable:	
	dw	0
	dw	3424,3232,3048,2880,2712,2560,2416,2280,2152,2032,1920,1812
	dw	1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,906
	dw	856,808,762,720,678,640,604,570,538,508,480,453
	dw	428,404,381,360,339,320,302,285,269,254,240,226
	dw	214,202,190,180,170,160,151,143,135,127,120,113
	dw	107,101,95,90,85,80,75,71,67,63,60,56
	dw	53,50,47,45,42,40,37,35,33,31,30,28

;-----------------------------------------------------------------------------
; Sinus wave table
;-----------------------------------------------------------------------------

sintable:
	db	0,25,50,74,98,120,142,162,180,197,212,225
	db	236,244,250,254,255,254,250,244,236,225
	db	212,197,180,162,142,120,98,74,50,25

;-----------------------------------------------------------------------------
; Copyright Strings
;-----------------------------------------------------------------------------

Credits:
	db      'Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan'
	db      10,13,'$'
MsgNotFound:
	db	'Sound Blaster not found!',10,13,'$'
MsgModErr:
	db 	'Error loading Module file!',10,13,'$'
MsgInitErr:
	db 	'Error initializing the sound system!',10,13,'$'
MsgFound:
	db	'Sound Blaster found at Address 2'
PortText:
	db	'?0h, IRQ '
IrqText:
	db	'?, DMA '
DmaText:
	db	"?.",10,13,'$'

msg_usage:
	db	'Tiny MOD Player v2 for MSDOS by Erdogan Tan. '
	db	'September 2017.',10,13
	db	'usage: tmodplay filename.mod', 10, 13, '$'
	db	'30/09/2017'
	db	0
	db 	'$'

align 4

bss_start:

ABSOLUTE bss_start

;=============================================================================
;		UNITIALIZED DATA
;=============================================================================

;-----------------------------------------------------------------------------
; Sound Blaster driver data
;-----------------------------------------------------------------------------

; Voices programmable parameters

MODVoiceTable:

voicepos:	resd MAXVOICES
voiceend:	resd MAXVOICES
voiceloop:	resd MAXVOICES
voicefrac:	resd MAXVOICES
voicepitch:	resd MAXVOICES
voicevolume:	resd MAXVOICES

; Internal driver data

mixbuffer:	resw 1	; mixing buffer address
boosttable:	resw 1	; boosting table address
voltable:	resw 1	; volume table address
numvoices:	resw 1	; number of active voices
mixfreq:	resw 1	; playback frequency
ioaddr:		resw 1	; card I/O port address
irqnum:		resb 1	; card IRQ level
drqnum:		resb 1	; card DMA channel
timerproc:	resw 1	; timer callback address
timeracc:	resw 1	; timer callback accumulator
timerspeed:	resw 1	; timer callback speed
;bufsel:	resw 1	; DOS memory block selector
bufptr:		resw 1	; DMA buffer address
bufoff: 	resw 1	; double buffer offset
oldirqoff:	resw 1	; old IRQ vector address
oldirqsel:	resw 1	
oldtimeroff:	resw 1	; old timer IRQ0 vector address
oldtimersel:	resw 1	
oldtimeracc:	resw 1	; old timer accumulator
manualmode:	resb 1	; timer/manual polling mode
playing:	resb 1 	; playing/stopped status

;-----------------------------------------------------------------------------
; Module Loader data
;-----------------------------------------------------------------------------

Header:		resb	MODHeader.size ; (structure is in 'modload.c')
Song:		resb	module.size    ; (structure is in 'MODPLAY.ASM')	

alignb 4

;-----------------------------------------------------------------------------
; Module Player data
;-----------------------------------------------------------------------------

; 29/09/2017 (MSDOS modifications)

moduleptr:	resw 1	; current module address
		resw 1
pattptr:	resd 1	; current playing pattern address
orderpos:	resb 1	; order position
orderlen:	resb 1	; order length
pattrow:	resb 1	; pattern row
tempo:		resb 1	; tempo
tempocount:	resb 1	; tempo counter
bpm:		resb 1	; beats per minute
musicvolume:	resb 1	; music channels volume
samplevolume:	resb 1	; sample channels volume
numtracks:	resw 1	; number of tracks
tracks:		resb track.size * MAXVOICES

pitchtable:	resd 3425 ; period to pitch table

;-----------------------------------------------------------------------------
; Etc.
;-----------------------------------------------------------------------------

SbPort:		resw 1  ; = [ioaddr] ; optional data for program version!		
SbIrq:		resb 1  ; = [irqnum] ; optional data for program version!
SbDma:		resb 1  ; = [drqnum] ; optional data for program version!

FileHandle:	resw 1	; MOD (or WAV) File Handle/Descriptor

num_patts:	
		resb 1	; number of patterns
		resb 1

LoopStart:	resw 1 
LoopLength:	resw 1

alignb 4

; 29/09/2017 	; 32 bits linear buffer addresses
dma_buffer:	resd 1	; DMA Buffer address
pattern_buffer:	resd 1	; Pattern Buffer address ; max. 128*2048 bytes
sample_buffer:	resd 1  ; At the end of pattern buffer

WaveInfo:
		resb WAVHeader.size

;mod_file_name:
;		resb 80

alignb 16

bss_end:	