;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
;                Tiny MOD Player for Watcom C/C++32 and DOS/4GW
;                      Version 2.11a  June 5th, 1994
;
;                      Copyright 1993,94 Carlos Hasan
;²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²
;		     The Psychic Monks present...
;
; ŪŪŪŪŪŪŪŪŪŪŪŪ			    ŪŪŪŪŪŪŪÜ
; ßßßßŪŪŪŪßßßß			    ŪŪŪŻ ßŪŪŪ
;     ŪŪŪŪ			    ŪŪŪŻ  ŪŪŪ ŪŪŪŪ
;     ŪŪŪŪ  ŽŪŪ  ŪŪŪÜŪŪÜ  ŪŪŪ  ŪŪŪ  ŪŪŪŻ ÜŪŪŪ  ŽŪŪ    ŪŪŪŪŪÜ  ŪŪŪ  ŪŪŪ
;     ŪŪŪŪ	 ŪŪŪßßŪŪŪ ŪŪŪ  ŪŪŪ  ŪŪŪŪŪŪŪß   ŽŪŪ	  ŪŪŻ ŪŪŪ  ŪŪŪ
;     ŪŪŪŪ ŪŪŪŪ  ŪŪŪ  ŪŪŪ ŪŪŪ  ŪŪŪ  ŪŪŪŻ       ŽŪŪ   ÜŪŪŪŪŪŪŻ ŪŪŪ  ŪŪŪ
;     ŪŪŪŪ  ŽŪŪ  ŪŪŪ  ŪŪŪ  ßŪŪŪŪŪŪ  ŪŪŪŻ       ŽŪŪ  ŪŪŪ  ŽŪŪŻ  ßŪŪŪŪŪŪ
;     ŪŪŪŪ  ŽŪŪ  ŪŪŪ  ŪŪŪ      ŪŪŻ  ŪŪŪŻ       ŽŪŪ  ŪŪŪ  ŽŪŪŻ	   ŪŪŻ
;     ŪŪŪŪ ŽŪŪŪŪ ŪŪŪ  ŪŪŪ  ŪŪŪŪŪß   ŪŪŪŻ      ŽŪŪŪŪ  ßŪŪŪŪßŪŪŪ ŪŪŪŪŪß
;
;		       Sound Blaster Module Player
;			      Version 2.11
;
;	 The first public digital sound player routines for use with
;	      Watcom C/C++32 Compiler and DOS/4GW DOS Extender
;		      and Borland C++ 3.1 Compiler
;
;		     Copyright (c)1994	Carlos Hasan
;
;			      (a.k.a. Pel/PM)
;
;
;
;
;Introduction
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;1.1	 What is Tiny Player?
;
;	 The Tiny Module Player is a system by which you can play digital
;music in your software programs. It was designed for the Watcom C/C++
;C compiler and the DOS/4GW Rational Systems DOS Extender. Also, It was
;translated to 16-bits to work with Borland C++ 3.1 compiler. It only
;supports the Soundblaster(tm) or compatible sound cards.
;
;
;1.2	 Hardware requirements
;
;	 The requirements for this system are a 386 computer and the
;Soundblaster(tm) or compatible sound card. It is much better if you have a
;fast 386 because you can achive higher mixing rates.
;
;
;1.3	 Smileware and contributions
;
;	 This program has been released as Smileware, which means you can use
;this program freely. Anyone can try it out without paying for the software.
;	 However, if you find this program useful and you want to use it
;indefinitely, I will be expecting some contributions. Also, if you are using
;this program, do not forget to give me credits in your productions.
;	 For those which are expecting GUS support in the next release of this
;sound player, please send contributions so I can buy a GUS sound card. :)
;	 For contributions, comments, suggestions, etc use the following
;mail addresses to contact the author:
;
;	 Snail Mail:
;		 Carlos Hasan
;		 Lo Encalada 144, Depto 22
;		 „u¤oa, Santiago
;		 CHILE
;
;	 Internet E-Mail:
;		 chasan@cec.uchile.cl
;		 cvaldovi@dcc.uchile.cl
;
;
;
;Using the Player
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;2.1	 Playing music modules
;
;	 The player is pretty easy to use. First, you must load any Protracker
;or Fastracker music module file using the loading routines. To play the music
;module you need the Soundblaster(tm) configuration parameters. To ease things
;there is a routine called MODDetect which will automatically detect the card
;configuration parameters (I/O port address, IRQ level and DMA channel). This
;routine may fail on some systems, so it is always safer to ask the user those
;hardware parameters.
;	 Now, call the routine MODPlayModule to start playing the music file
;in the background. The routine parameters are the music module address, the
;mixing rate, sound card parameters and polling mode.
;	 If you do not want to play music in the background, but use the sound
;effect channels, you can call this routine passing NULL instead of a valid
;music module address.
;	The MODStopModule will deinitialize the sound system, and should be
;called before exiting your program. This routine will close all the sound
;channels (music and sound effect channels).
;
;
;2.2	 Playing sound effects
;
;	 The player supports up to 8 sound channels. The program itself uses
;the first channels to play the music module. For example, for a normal
;4 channels module file, the channels 0, 1, 2, and 3 will be used to play
;the music module. You can use the others unused channels to play sound
;effects and the like.
;	 The digital sound samples can be loaded from disk using the routine
;MODLoadSample which can read standard RIFF/WAV sample files, only 8-bit mono
;sound files are supported. After loading the sample, you can play it using
;the routine MODPlaySample. There are also routines to change the frequency
;and volume of the sound channels.
;	 You can control the overall volume of the music channels and the
;sound effects channels using two routines called MODSetMusicVolume and
;MODSetSampleVolume. These routines are very useful to do volume fades of
;the music, and for games which are playing sound effects over the music.
;
;
;2.3	 Closing Words
;
;	 First, I'd like to thank Vernor for helping me to write this document,
;and to Necros for the wonderful 8 channels module specially maded for this
;release. Also, I send greetings to all the others Psychic Monks dudes around.
;I should say that the Protracker tune was created from a S3M file, so it does
;not sound as good as the original Scream Tracker 3.01 module file.
;	 I really hope that you find this program useful. It is not the best
;or fastest player around, but the multiple sound channels capabilities should
;make it very useful to create games, demos, etc.
;
;
;
;Reference Guide
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;
;3.1 LISTING OF ROUTINES
;
; MODPlayModule
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Start playing a Modulefile.
;
; Prototype:	 int MODPlayModule(Song,Chans,Rate,Port,IRQ,DRQ,Mode)
;
; Parameters:	 Song	 - Address of the Module
;		 Chans	 - Number of channels (1-8)
;		 Rate	 - Mixing speed in Hertz (4kHz-22kHz)
;
; Returns:	 On success, return a zero value.
;		 On error, return a non zero value.
;
; Remarks:	 This function will initialize the and
;		 will start playing the module file immediately. The module
;		 file must be a 4,6 or 8 channels Protracker or FastTracker
;		 music module file.
;		 The player supports two kinds of channels, music and sample
;		 audio channels. The music channels are used by the player
;		 to play the module, and the sample channels are used for
;		 sound effects and the like.
;		 The channels voices 0 to N-1 are used for music channels,
;		 where N is the number of channels of the module file.
;
;
; MODPoll
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Polls the music system in manual mode.
;
; Prototype:	 void MODPoll(location) // pass the location in output buffer
;
; Parameters:	 None.
;
; Returns:	 None.
;
;
; MODStopModule
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Stop playing the current Modulefile.
;
; Prototype:	 void MODStopModule()
;
; Parameters:	 None.
;
; Returns:	 None.
;
; Remarks:	 This function shuts down the playing system. Must be called
;		 before exiting the user program.
;
;
; MODPlaySample
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Play instrument at specified period and volume.
;
; Prototype:	 void MODPlaySample(Voice,Instr)
;
; Parameters:	 Voice	 - Audio channel number (0-7)
;		 Instr	 - Instrument address
;
; Returns:	 None.
;
; Remarks:	 This function is useful to play samples over music. The sample
;		 structure holds the period, volume and the address of the 8-bit
;		 signed samples to be played in the channel.
;		 The amiga period value can be translated to hertz using the
;		 following formula:  Hertz = 8363*428/Period
;
;
; MODStopSample
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Stop a sample channel.
;
; Prototype:	 void MODStopSample(Voice)
;
; Parameters:	 Voice	 - Audio channel number (0-7)
;
; Remarks:	 This function will stop the specified voice setting the channel
;		 volume to zero. The voice should be a sample channel.
;
;
; MODSetPeriod
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Set the sample channel period value.
;
; Prototype:	 void MODSetPeriod(Voice,Period)
;
; Parameters:	 Voice	 - Audio channel number (0-7)
;		 Period  - Amiga Period (113-856)
;
; Returns:	 None.
;
; Remarks:	 This function will change the current frequency of the sample
;		 channel. The voice should be a sample channel.
;
;
; MODSetVolume
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Set the sample channel volume.
;
; Prototype:	 void MODSetVolume(Voice,Volume)
;
; Parameters:	 Voice	 - Audio channel number (0-7)
;		 Volume  - Volume (0-64)
;
; Returns:	 None.
;
; Remarks:	 This function will change the channel volume. The voice should
;		 be a sample channel.
;
;
; MODSetMusicVolume
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Set the global music channels volume.
;
; Prototype:	 void MODSetMusicVolume(Volume)
;
; Parameters:	 Volume  - Volume (0-255)
;
; Returns:	 None.
;
; Remarks:	 This function will set the global volume for all the music
;		 channels.
;
;
; MODSetSampleVolume
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Set the global sample channels volume.
;
; Prototype:	 void MODSetSampleVolume(Volume)
;
; Parameters:	 Volume  - Volume (0-255)
;
; Returns:	 None.
;
; Remarks:	 This function will set the global volume for all the sample
;		 channels.
;
;
; MODLoadModule
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Load Module file from disk.
;
; Prototype:	 Module *MODLoadModule(Path)
;
; Parameters:	 Path	 - Module file name.
;
; Returns:	 The module structure address or NULL of error.
;
; Remarks:	 This function loads a 4,6 or 8 channels standard Protracker
;		 or Fastracker module music file from disk.
;
;
; MODFreeModule
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Free Module file from memory.
;
; Prototype:	 void MODFreeModule(Song)
;
; Parameters:	 Song	 - Module file address.
;
; Returns:	 None.
;
; Remarks:	 This function frees the module music from memory. You cannot
;		 free a module while it is being played.
;
;
; MODLoadSample
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Load sample file from disk.
;
; Prototype:	 Sample *MODLoadSample(Path)
;
; Parameters:	 Path	 - WAV file name.
;
; Returns:	 The sample structure address or NULL of error.
;
; Remarks:	 This function loads a RIFF/WAV sample file, the player only
;		 supports 8-bit mono unsigned sample file formats.
;
;
; MODFreeSample
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Function:	 Free Sample file from memory.
;
; Prototype:	 void MODFreeSample(Instr)
;
; Parameters:	 Instr	 - Sample file address.
;
; Returns:	 None.
;
; Remarks:	 This function frees the sample file from memory. You cannot
;		 free a sample while it is being played.
;


ideal
p386
model   flat,c
smart

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; EQUATES AND PUBLICS
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±

MAXVOICES = 8                           ; number of voices
DMABUFLEN = 4096			; DMA buffer length (multiple of 64)
VOLBUFLEN = 66*256                      ; volume table length
MIXBUFLEN = 2*DMABUFLEN+2048            ; mixing/boosting buffer length

global	MODGetSetBuff:proc	; get/set buffer.
global	MODSizeDMA:proc
global  MODPlayModule:proc
global  MODStopModule:proc
global  MODPlaySample:proc
global  MODStopSample:proc
global  MODSetPeriod:proc
global  MODSetVolume:proc
global  MODSetMusicVolume:proc
global  MODSetSampleVolume:proc
global  MODPoll:proc
global  MODVoiceTable:dword

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; STRUCTURES
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±

struc   module                          ; module structure
  numtracks     dw      ?               ; number of tracks
  orderlen      dw      ?               ; order length
  orders        db      128 dup (?)     ; order list
  patterns      dd      128 dup (?)     ; pattern addresses
  sampptr       dd      32 dup (?)      ; sample start addresses
  sampend       dd      32 dup (?)      ; sample end addresses
  samploop      dd      32 dup (?)      ; sample loop point addresses
  sampvolume    db      32 dup (?)      ; sample default volumes
ends    module

struc   sample                          ; sample structure
  period        dw      ?               ; default period
  volume        dw      ?               ; default volume
  datalen       dd      ?               ; sample data length
  dataptr       dd      ?               ; sample data address
ends    sample

struc   track                           ; track structure
  note          dw      ?               ; note index
  period        dw      ?               ; period value
  inst          db      ?               ; instrument
  volume        db      ?               ; volume
  effect        dw      ?               ; effect
  destperiod    dw      ?               ; toneporta wanted period
  tonespeed     db      ?               ; toneporta speed
  vibparm       db      ?               ; vibrato depth/rate
  vibpos        db      ?               ; vibrato wave position
  tremparm      db      ?               ; tremolo depth/rate
  trempos       db      ?               ; tremolo wave position
                db      ?               ; alignment
  arptable      dw      3 dup (?)       ; arpeggio periods
ends    track

;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; DATA
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
; Module Player data
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
udataseg

moduleptr       dd      ?               ; current module address
pattptr         dd      ?               ; current playing pattern address
orderpos        db      ?               ; order position
orderlen        db      ?               ; order length
pattrow         db      ?               ; pattern row
tempo           db      ?               ; tempo
tempocount      db      ?               ; tempo counter
bpm             db      ?               ; beats per minute
musicvolume     db      ?               ; music channels volume
samplevolume    db      ?               ; sample channels volume
numtracks       dw      ?               ; number of tracks
tracks          track   MAXVOICES dup (?)

pitchtable      dd      3425 dup (?)    ; period to pitch table

; Amiga period table
dataseg

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

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
; Sound Blaster driver data
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
udataseg

; Voices programmable parameters

label MODVoiceTable dword

voicepos        dd      MAXVOICES dup (?)
voiceend        dd      MAXVOICES dup (?)
voiceloop       dd      MAXVOICES dup (?)
voicefrac       dd      MAXVOICES dup (?)
voicepitch      dd      MAXVOICES dup (?)
voicevolume     dd      MAXVOICES dup (?)

; Internal driver data
dataseg

mixbuffer       dd      ?               ; mixing buffer address
boosttable      dd      ?               ; boosting table address
voltable        dd      ?               ; volume table address
numvoices       dw      ?               ; number of active voices
mixfreq         dw      ?               ; playback frequency
timerproc       dd      ?               ; timer callback address
timeracc        dd      ?               ; timer callback accumulator
timerspeed      dd      ?               ; timer callback speed
datasel         dw      ?               ; flat model data selector
bufsel          dw      ?               ; DOS memory block selector
bufptr          dd      ?               ; DMA buffer address
bufoff          dd      ?               ; double buffer offset
playing         db      0               ; playing/stopped status


;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
; CODE
;±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±±
codeseg

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; Copyright Strings
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°

db      'Tiny MOD Player V2.11 Copyright 1993,94 Carlos Hasan',0
db      'Compiled on: ',??date,' ',??time,0

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
; Module Player stuff
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
proc	MODGetSetBuff buff:dword
	mov	eax,[buff]
	mov	[mixbuffer],eax
	mov	eax,(DMABUFLEN+VOLBUFLEN+MIXBUFLEN+15)
	ret
	endp

proc	MODSizeDMA
	mov	eax,DMABUFLEN
	ret
	endp

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODPlayModule - start playing a music module
; In:
;  Song  = module address
;  Chans = number of channels
;  Rate  = playback rate
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	MODPlayModule Song:dword,Chans:dword,Rate:dword,playbuf:dword
        pushad

; setup the music module address

        mov     esi,[Song]
        mov     [moduleptr],esi

; setup the sound card driver

        mov     ax,[word Rate]
        mov     bl,[byte Chans]
        call    mixinit
        jc      playmoduled0

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

        movzx   ebx,ax
        mov     eax,8363*428
        xor     edx,edx
        shld    edx,eax,16
        shl     eax,16
        div     ebx
        mov     esi,eax
        lea     edi,[pitchtable]
        mov     ecx,3425
        xor     ebx,ebx
playmodulel0:
        inc     ebx
        xor     edx,edx
        mov     eax,esi
        div     ebx
        mov     [edi],eax
        add     edi,4
        loop    playmodulel0

; setup global volumes for music and sample channels

        mov     [musicvolume],255
        mov     [samplevolume],255

; clear the module player track structures

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        lea     edi,[tracks]
        mov     ecx,MAXVOICES*(size track)
        xor     al,al
        rep     stosb
        pop     es

; check if there is a module to playback

        xor     eax,eax
        mov     [numtracks],ax

        mov     esi,[moduleptr]
        test    esi,esi
        clc
        je      playmoduled0

; setup player interpreter variables

        mov     esi,[moduleptr]
        mov     ax,[esi+module.orderlen]
        mov     [orderlen],al
        mov     ax,[esi+module.numtracks]
        mov     [numtracks],ax
        mov     [tempo],6
        mov     [bpm],125
        mov     [orderpos],0
        mov     [tempocount],0
        mov     [pattrow],40h

; setup the player callback timer routine

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

playmoduled0:
	mov	esi,[playbuf]
	mov	eax,[bufptr]	; dma transfer address.
	mov	[esi],eax	; set the address
        popad
        sbb     eax,eax
        ret
endp    MODPlayModule

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODStopModule - shut down the music system
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODStopModule
        pushad
        call    mixstoptimer            ; stop the timer callback
        call    mixdone                 ; shutdown the SB stuff
        popad
        ret
endp    MODStopModule

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODPoll - polls the music system in manual mode
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc	MODPoll outloc:dword
        pushad
        cmp     [playing],0             ; polling mode (and if the driver
        je      modpolld0               ; is active).
	mov	eax,[outloc]		; current output location.
        call    mixpoll
modpolld0:
        popad
        ret
endp    MODPoll

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODPlaySample - play sample instrument
; In:
;  voice  = voice number
;  sample = sample address
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODPlaySample Voice:dword,SamplePtr:dword
        pushad
        cli

; get the voice number and track address

        mov     ebx,[Voice]
        mov     edi,[SamplePtr]
        mov     esi,ebx
        imul    esi,size track

; set the voice pitch value

        movzx   eax,[edi+sample.period]
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax

; set the voice sample parameters

        mov     eax,[edi+sample.dataptr]
        mov     [4*ebx+voicepos],eax
        add     eax,[edi+sample.datalen]
        mov     [4*ebx+voiceend],eax
        mov     [4*ebx+voiceloop],eax

; set the voice and track volumes

        mov     ax,[edi+sample.volume]
        mov     [esi+tracks.volume],al
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah

        sti
        popad
        ret
endp    MODPlaySample

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODStopSample - stop the playing sample
; In:
;  voice = voice number
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODStopSample Voice:dword
        pushad
        cli

; get the voice number

        mov     ebx,[Voice]
        xor     eax,eax

; clear the voice sample parameters

        mov     [4*ebx+voicepos],eax
        mov     [4*ebx+voiceend],eax
        mov     [4*ebx+voiceloop],eax

        sti
        popad
        ret
endp    MODStopSample

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODSetPeriod -  set the voice period value
; In:
;  voice = voice number
;  period = period value (113-856)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODSetPeriod Voice:dword,Period:dword
        pushad
        cli

; get the voice number and period value

        mov     ebx,[Voice]
        mov     eax,[Period]

; set the voice pitch value

        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax

        sti
        popad
        ret
endp    MODSetPeriod

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODSetVolume -  set the voice volume level
; In:
;  voice = voice number
;  volume = volume level (0-64)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODSetVolume Voice:dword,Volume:dword
        pushad
        cli

; get the voice number and track address

        mov     ebx,[Voice]
        mov     eax,[Volume]
        mov     esi,ebx
        imul    esi,size track

; set the voice and track volume

        mov     [esi+tracks.volume],al
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah

        sti
        popad
        ret
endp    MODSetVolume

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODSetMusicVolume - set the global music volume
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODSetMusicVolume Volume:dword
        pushad
        cli

; set new music volume

        mov     eax,[Volume]
        mov     [musicvolume],al

; update all the music voices

        lea     esi,[tracks]
        xor     ebx,ebx
setmusicvolumel0:
        mov     al,[esi+track.volume]
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        add     esi,size track
        inc     ebx
        cmp     bx,[numtracks]
        jb      setmusicvolumel0

        sti
        popad
        ret
endp    MODSetMusicVolume

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; MODSetSampleVolume - set the global sample volume
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
proc    MODSetSampleVolume Volume:dword
        pushad
        cli

; set the sample volume

        mov     eax,[Volume]
        mov     [samplevolume],al

; update all the sample voices

        lea     esi,[tracks]
        xor     ebx,ebx
setsamplevolumel0:
        cmp     bx,[numtracks]
        jb      setsamplevolumef0
        mov     al,[esi+track.volume]
        mul     [samplevolume]
        mov     [byte 4*ebx+voicevolume],ah
setsamplevolumef0:
        add     esi,size track
        inc     ebx
        cmp     bx,MAXVOICES
        jb      setsamplevolumel0

        sti
        popad
        ret
endp    MODSetSampleVolume


;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; pollmodule - polls the module player
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
pollmodule:
        pushad
        dec     [tempocount]            ; decrease the tempo counter
        jle     pollmodulef0
        lea     esi,[tracks]            ; while in the same pattern row
        xor     ebx,ebx                 ; update the track effects.
pollmodulel0:
        call    updatechannel
        add     esi,size track
        inc     ebx
        cmp     bx,[numtracks]
        jb      pollmodulel0
        popad
        ret

pollmodulef0:                           ; advance to the next pattern row.
        mov     al,[tempo]              ; update the tempo counter
        mov     [tempocount],al
        mov     edx,[moduleptr]         ; get the module and pattern address
        mov     edi,[pattptr]
        cmp     [pattrow],40h           ; need to advance to the next order?
        jb      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      pollmodulef1
        xor     al,al
        mov     [orderpos],al
pollmodulef1:
        inc     [orderpos]              ; get the new pattern address
        movzx   eax,[edx+eax+module.orders]
        mov     edi,[edx+4*eax+module.patterns]
pollmodulef2:
        inc     [pattrow]               ; increase pattern row number
        lea     esi,[tracks]
        xor     ebx,ebx                 ; read and interpret the next
pollmodulel1:                           ; pattern row of events
        call    readchannel
        add     esi,size track
        add     edi,4
        inc     ebx
        cmp     bx,[numtracks]
        jb      pollmodulel1
        mov     [pattptr],edi           ; save pattern row address
        popad
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; readchannel - read the next note event from the pattern sheet
; In:
;  EBX = voice number
;  ESI = track address
;  EDI = pattern address
;  EDX = module address
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
readchannel:
        pushad

; check for new sample number . . .

        mov     al,[edi+1]
        test    al,al
        je      readchannelf0
        mov     [esi+track.inst],al
        movzx   eax,al
        mov     al,[edx+eax+module.sampvolume]
        mov     [esi+track.volume],al
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah

; check for new note pitch . . .

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

; check the new track effect . . .

readchannelf1:
        mov     dx,[edi+2]
        mov     [esi+track.effect],dx
        movzx   eax,dh
        and     al,0Fh
        call    [4*eax+efxtable]

        popad
        ret

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

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
; Protracker effects stuff
;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; Effect jump tables
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°

        align   4

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

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

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

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

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

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

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

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; efxvibrato - pitch vibrato
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
efxvibrato:
        mov     al,[esi+track.vibparm]
        mov     ah,al
        and     ax,0F00Fh
        test    dl,0Fh
        jne     efxvibratof0
        or      dl,al
efxvibratof0:
        test    dl,0F0h
        jne     efxvibratof1
        or      dl,ah
efxvibratof1:
        mov     [esi+track.vibparm],dl
        mov     [esi+track.effect],dx
        ret
efxvibrato2:
        mov     dh,dl
        and     dx,0F00Fh
        shr     dh,2
        mov     al,[esi+track.vibpos]
        add     [esi+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     efxvibratof2
        neg     ax
efxvibratof2:
        add     ax,[esi+track.period]
        cmp     ax,28
        jge     efxvibratof3
        mov     ax,28
efxvibratof3:
        cmp     ax,3424
        jle     efxvibratof4
        mov     ax,3424
efxvibratof4:
        movzx   eax,ax
        mov     eax,[4*eax+pitchtable]
        mov     [4*ebx+voicepitch],eax
        ret

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

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

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; efxtremolo - volume vibrato
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
efxtremolo:
        mov     al,[esi+track.tremparm]
        mov     ah,al
        and     ax,0F00Fh
        test    dl,0Fh
        jne     efxtremolof0
        or      dl,al
efxtremolof0:
        test    dl,0F0h
        jne     efxtremolof1
        or      dl,ah
efxtremolof1:
        mov     [esi+track.tremparm],dl
        mov     [esi+track.effect],dx
        ret
efxtremolo2:
        mov     dh,dl
        and     dx,0F00Fh
        shr     dh,2
        mov     al,[esi+track.trempos]
        add     [esi+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     efxtremolof2
        neg     ax
efxtremolof2:
        add     al,[esi+track.volume]
        jge     efxtremolof3
        xor     al,al
efxtremolof3:
        cmp     al,40h
        jle     efxtremolof4
        mov     al,40h
efxtremolof4:
        mul     [musicvolume]
        mov     [byte 4*ebx+voicevolume],ah
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; efxsampoffset - set the sample offset
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
efxsampoffset:
        movzx   eax,[esi+track.inst]
        mov     esi,[moduleptr]
        mov     eax,[esi+4*eax+module.sampptr]
        movzx   edx,dl
        shl     edx,8
        add     eax,edx
        mov     [4*ebx+voicepos],eax
        ret

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

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; efxpattjump - jump to order pattern
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
efxpattjump:
        mov     [orderpos],dl
        mov     [pattrow],40h
        ret

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

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; efxbreak - break pattern
; In:
;  EBX = voice number
;  ESI = track address
;  DL = effect parameter
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
efxbreak:
        mov     [pattrow],40h
        ret

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

;°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±°±
; 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:
        pushad

        cmp     [playing],0
        stc
        jne     mixinitd0

; setup sound card parameters

        xor     bh,bh
        mov     [mixfreq],ax
        mov     [numvoices],bx

; setup timer and double buffer variables

        mov     [timerproc],offset nulltimer
        xor     eax,eax
        mov     [bufoff],eax
        mov     [timeracc],eax
        mov     [timerspeed],256

; clear voice parameters

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        lea     edi,[voicepos]
        mov     ecx,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.

; set the address of the mixing buffer and boosting table
	mov	eax,[mixbuffer]

        add     eax,2*DMABUFLEN
        mov     [boosttable],eax
        add     eax,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     mixinitf0
        mov     edx,eax
        add     eax,VOLBUFLEN
mixinitf0:
        add     edx,255
        xor     dl,dl
        mov     [bufptr],eax
        mov     [voltable],edx

; clear DMA buffer with centered samples

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        mov     edi,[bufptr]
        mov     ecx,DMABUFLEN
        mov     al,80h
        rep     stosb
        pop     es

; build volume table and boosting table

        mov     cl,6
        mov     edi,[voltable]
        xor     bx,bx
mixinitl0:
        mov     al,bl
        imul    bh
        sar     ax,cl
        mov     [edi],al
        inc     edi
        inc     bl
        jne     mixinitl0
        inc     bh
        cmp     bh,40h
        jbe     mixinitl0

        push    es
        mov     ax,ds
        mov     es,ax
        cld
        mov     edi,[boosttable]
        mov     ecx,768
        xor     ax,ax
        rep     stosb
        mov     ecx,512
mixinitl1:
        mov     [edi],ah
        add     ax,80h
        inc     edi
        loop    mixinitl1
        mov     ecx,768
        dec     al
        rep     stosb
        pop     es

; set driver playing status

mixinitf1:
        mov     [playing],1
        clc

mixinitd0:
        popad
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixdone - deinitialize the sound driver
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixdone:
        pushad

        cmp     [playing],1
        jne     mixdoned0

; set driver stopped status

        mov     [playing],0

mixdoned0:
        popad
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixsettimerproc - set the timer procedure
; In:
;  EDX = timer routine address
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixsettimerproc:
        mov     [timerproc],edx         ; set the timer callback address
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixstarttimer - start the timer at the specified speed
; In:
;  DL = timer speed in beats per minute (BPMs)
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixstarttimer:
        push    eax
        push    ebx
        push    edx
        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
        movzx   eax,ax
        mov     [timerspeed],eax
        pop     edx
        pop     ebx
        pop     eax
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixstoptimer - stop the timer routine
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixstoptimer:
        mov     [timerproc],offset nulltimer
nulltimer:
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixvoice - mixes the voice samples
; In:
;  EBX = voice number (*4)
;  ECX = number of samples
;  EDI = buffer address
; Out:
;  EDI = buffer end address
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixvoice:

        macro   mixcode OPCODE
        local   mixcodel0,mixjmptable

        push    eax
        push    ebx
        push    ecx
        push    edx
        push    ebp
        push    esi

        push    ebx
        mov     esi,[ebx+voicepos]
        mov     dl,[byte ebx+3+voicefrac]
        mov     dh,[byte ebx+1+voicepitch]
        movzx   ebp,[word ebx+2+voicepitch]
        mov     ebx,[ebx+voicevolume]
        mov     bh,bl
        add     ebx,[voltable]

        movzx   eax,cl
        and     al,31
        shr     ecx,5
        lea     edi,[edi+4*eax-4*32]
        jmp     [4*eax+mixjmptable]

        align   4
mixcodel0:
        I=0
        rept    32
CODESTART=$
        mov     bl,[esi]
        add     dl,dh
        movsx   eax,[byte ebx]
        adc     esi,ebp
        OPCODE  [edi+I],eax
CODELEN=$-CODESTART
        I=I+4
        endm
        add     edi,4*32
        dec     ecx
        jge     mixcodel0

        pop     ebx
        mov     [ebx+voicepos],esi
        mov     [byte ebx+3+voicefrac],dl

        pop     esi
        pop     ebp
        pop     edx
        pop     ecx
        pop     ebx
        pop     eax
        ret

        align   4
        label mixjmptable dword
        I=CODESTART+CODELEN
        rept    32
        dd      I
        I=I-CODELEN
        endm
        endm

        test    ebx,ebx
        je      mixvoicef0
        mixcode add
mixvoicef0:
        mixcode mov

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixvoices - mixes all the voices
; In:
;  EDI = buffer address
;  ECX = number of samples
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixvoices:
        pushad

        xor     ebx,ebx
mixvoicesl0:
        push    ebx
        push    ecx
        push    edi
        shl     ebx,2
        mov     ebp,ecx

mixvoicesl1:
        mov     ecx,ebp
        mov     edx,[ebx+voicepos]
        mov     eax,[ebx+voiceend]
        cmp     edx,eax
        jb      mixvoicesf0
        sub     edx,eax
        add     edx,[ebx+voiceloop]
        cmp     edx,eax
        jae     mixvoicesc0
        mov     [ebx+voicepos],edx

mixvoicesf0:
        sub     eax,edx
        shl     eax,16
        mov     edx,[ebx+voicefrac]
        shr     edx,16
        sub     eax,edx

        mov     edx,ecx
        mov     esi,[ebx+voicepitch]
        imul    edx,esi
        cmp     edx,eax
        jbe     mixvoicesf1
        dec     eax
        xor     edx,edx
        add     eax,esi
        adc     edx,edx
        div     esi
        mov     ecx,eax

mixvoicesf1:
        call    mixvoice
        sub     ebp,ecx
        jg      mixvoicesl1

        pop     edi
        pop     ecx
        pop     ebx
        inc     ebx
        cmp     bx,[numvoices]
        jb      mixvoicesl0

        popad
        ret

mixvoicesc0:
        test    ebx,ebx
        jne     mixvoicesc1
        push    es
        mov     ax,ds
        mov     es,ax
        xor     eax,eax
        cld
        rep     stosd
        pop     es

mixvoicesc1:
        pop     edi
        pop     ecx
        pop     ebx
        inc     ebx
        cmp     bx,[numvoices]
        jb      mixvoicesl0

        popad
        ret

;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
; mixpoll - updates the output buffer
; EAX -> current ouptut location.
;°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
mixpoll:
        pushad

; get the 32 bit mixing buffer address

        mov     ecx,DMABUFLEN/2
        mov     edi,[mixbuffer]

; check if we can fill the current half buffer with samples
        cmp     eax,ecx
        jae     mixpollf3
        cmp     [bufoff],ecx
        je      mixpollf4
        jmp     mixpolld0
mixpollf3:
        cmp     [bufoff],ecx
        je      mixpolld0

; fill the mixing buffer and polls the timer callback routine

mixpollf4:
        mov     eax,[timeracc]
        mov     ebp,ecx
mixpolll0:
        test    eax,eax
        jg      mixpollf0
        call    [timerproc]
        add     eax,[timerspeed]
mixpollf0:
        mov     ecx,eax
        add     ecx,63
        and     cl,not 63
        cmp     ecx,ebp
        jle     mixpollf1
        mov     ecx,ebp
mixpollf1:
        call    mixvoices
        lea     edi,[edi+4*ecx]
        sub     eax,ecx
        sub     ebp,ecx
        jg      mixpolll0
        mov     [timeracc],eax

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

        mov     ecx,DMABUFLEN/2
        mov     esi,[mixbuffer]
        mov     edi,[bufptr]
        add     edi,[bufoff]
        xor     [bufoff],ecx
        mov     ebx,[boosttable]
        add     ebx,1024
        shr     ecx,4
mixpolll2:
        I=0
        rept    4
        mov     eax,[esi+16*I+8]
        mov     dl,[eax+ebx]
        mov     eax,[esi+16*I+12]
        mov     dh,[eax+ebx]
        shl     edx,16
        mov     eax,[esi+16*I]
        mov     dl,[eax+ebx]
        mov     eax,[esi+16*I+4]
        mov     dh,[eax+ebx]
        mov     [edi+4*I],edx
        I=I+1
        endm
        add     esi,4*16
        add     edi,16
        dec     ecx
        jg      mixpolll2

mixpolld0:
        popad
        ret


end
