;                             GUSMOD v1.1
;	     By Joshua C. Jensen (CyberStrike of Renaissance)
;	     Copyright (C) 1992,1993  -  All Rights Reserved
;ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ

ideal
P286
model	Huge
jumps

stack	1024

MaxVoices	= 13

include "gusmod.inc"

segment MyCode

		assume	cs:Mycode,ds:MyData

PspAddress	dw	?

Start:
		mov    [cs:PspAddress],es
		mov	ax,es
		mov	bx,zzzzzseg
		sub	bx,ax
		add	bx,2
		mov	ah, 4ah
		int	21h

                mov     ax,Mydata
                mov     ds,ax
                mov     dx,offset MyTitle
		mov	ah,9
		int	21h

		call	U_DetectGUS
                jnb     @@FoundGUS
                mov     ah,9
                mov     dx,offset NoGUS
                int     21h
                jmp     @@Exit

@@FoundGUS:     call    U_Reset
                ; Scan the command line for the module name.
		mov	ds, [cs:PspAddress]
		mov	si, 128
		lodsb
		or	al,al			; Are we at a null?
		jz	@@noargv
@@MoreThanOne:	mov	bx,si
		xor	ah,ah
		add	bx,ax
@@TopLoop:	cmp	bx,si
		jz	@@noargv
		lodsb
		cmp	al,' '
		jz	@@TopLoop
@@GetFilename:	xor	ah,ah
		dec	si
		mov	cx,80
@@TopGet:	cmp	bx,si
		jz	@@StoreFilename
		lodsb
		cmp	al,'.'
		jnz	@@NotPeriod
		mov	ah,1
@@NotPeriod:	cmp	al,13
		jz	@@StoreFilename
		loop	@@TopGet
@@StoreFilename:or	ah,ah
		jnz	@@JustZero
		mov	[Byte si],'.'
		mov	[Byte si+1],'M'
		mov	[Byte si+2],'O'
		mov	[Byte si+3],'D'
		add	si,4
@@JustZero:	mov	[Byte si],0
		mov	[Byte si+1],'$'

		; Load the module.
		mov	dx, 130 		; Psp Command Tail + 2
		call	s_LoadModule
		jb	@@nofile
		jmp	@@BeginPlay

@@noargv:	mov	ax,MyData
		mov	ds,ax
		mov	dx,offset NoFileName
		mov	ah,9
		int	21h
		jmp	@@exit

@@nofile:	mov	ax,MyData
		mov	ds,ax
		mov	dx,offset NoFileFound
		mov	ah,9
		int	21h
		jmp	@@exit

@@BeginPlay:	mov	al,0
		call	U_ChangeInOut
		call	sd_SetMaxVolume
		call	s_PlayMusic
@@PollLoop:	mov	ah,1
		int	16h
		jnz	@@KeyHit
		jmp	@@PollLoop
@@KeyHit:	call	s_StopMusic
		call	U_Reset
@@Exit: 	call	s_CloseAllMusic
		mov	ax,4C00h
		int	21h

ends	MyCode

segment MyData
label   MyTitle byte
                db      '                              GUSMOD v1.1',10,13
		db	'             By Joshua C. Jensen (CyberStrike of Renaissance)',13,10
                db      '             Copyright (C) 1992,1993  -  All Rights Reserved',10,13
                db      'ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ$'
NoGUS		db	'Could not detect a Gravis Ultrasound card.',13,10,'$'
NoFileName	db	'Usage: GUSMOD filename.mod',13,10,'$'
NoFileFound	db	'File not found.',13,10,'$'

;
;
ends    MyData

segment         Sound
assume		cs:Sound,ds:Sound

Header:
songname	db	20 dup (0)
samples 	db	31*size SampleRec dup (0)
songlen 	db	0
restart 	db	0
sequences	db	128 dup (0)
mk		dd	0
HeaderSize	 =	$-Header
PatternLoc	dw	64 dup (0)
InsLoc		dd	32 dup (0)
NumPatterns	dw	0

IntStore	dd	0

Base	      dw      220h

proc    UDelay
        push    dx ax
        mov     dx,300h
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        in      al,dx
        pop     ax dx
        ret
endp    UDelay

; BX:CX Set to whatever.
proc    U_Peek
        push    dx
	mov	dx,[cs:u_Command]
        mov     al,43h
        out     dx,al
        inc     dx                      ; 104h
        mov     ax,cx
        out     dx,ax
        dec     dx                      ; 103h
        mov     al,44h
        out     dx,al
        add     dx,2
        mov     al,bl
        out     dx,al
        add     dx,2
        in      al,dx
        pop     dx
        ret
endp    U_Peek

; BX:CX Set to whatever.
; AX Value to poke
proc    U_Poke
        push    dx ax
	mov	dx,[cs:u_Command]
        mov     al,43h
        out     dx,al
        inc     dx
        mov     ax,cx
        out     dx,ax
        dec     dx
        mov     al,44h
        out     dx,al
        add     dx,2
        mov     al,bl
        out     dx,al
        add     dx,2
        pop     ax
        out     dx,al
	in	al,dx
	pop	dx
        ret
endp    U_Poke


; DX - Base.
proc    U_Probe
	mov	dx,[cs:u_Command]
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,0
        out     dx,al
        call    UDelay
        call    UDelay
        sub     dx,2                    ; 103h
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,1
        out     dx,al
	call	UDelay
	call	UDelay
	mov	ax,0AAh
        mov     bx,0
        mov     cx,0
        call    U_Poke
        mov     ax,055h
        mov     bx,1
        call    U_Poke
        mov     bx,0
        call    U_Peek
        push    ax
        mov     ax,0
        call    U_Poke
        sub     dx,2                    ; 103h
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,0
        out     dx,al
        pop     ax
        cmp     al,0AAh
        jnz     @@Nope
        clc
        ret
@@Nope: stc
        ret
endp    U_Probe

proc    U_Reset
	mov	dx,[cs:u_Command]
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,1
        out     dx,al
        call    UDelay
        sub     dx,2
        mov     al,4Ch
        out     dx,al
        add     dx,2                    ; 105h
        mov     al,7
        out     dx,al
        sub     dx,2
        mov     al,0Eh
        out     dx,al
        add     dx,2
	mov	al,MaxVoices
	or	al,0C0h
        out     dx,al

	mov	cx,0
@@VoiceClearLoop:
	mov	dx,[cs:u_Voice]
	mov	al,cl
	out	dx,al
	inc	dx
	mov	al,0
	out	dx,al
	add	dx,2
	mov	al,3			; Turn voice off
	out	dx,al
	sub	dx,2
	mov	al,0Dh
	out	dx,al
	add	dx,2
	mov	al,3
	out	dx,al
	inc	cx
	cmp	cx,32
	jnz	@@VoiceClearLoop
	ret
endp    U_Reset

; CX:AX  - Number
proc    RShift
        mov     bx,cx
        shr     ax,7
        shr     cx,7
        shl     bx,9
        or      ax,bx
        ret
endp    RShift

; AX - Voice
; Returns:  DX:AX - Position
proc	U_ReadPos
	mov	dx,[cs:u_Voice]
	out	dx,al
	inc	dx		; 103h
	mov	al,8ah
	out	dx,al
	inc	dx		; 104h
	in	ax,dx		; TEMP0
	mov	cx,ax
	dec	dx		; 103h
	mov	al,8bh
	out	dx,al
	inc	dx		; 104h
	in	ax,dx		; TEMP1
	xor	dx,dx
	mov	bx,cx
	shl	cx,7
	shl	dx,7
	shr	bx,9
	or	dx,bx
	shr	ax,9
	and	ax,7Fh
	or	cx,ax
	mov	ax,cx
	ret
endp	U_ReadPos

; AX-Mixer control
;  bit 0: 0=linein on, 1=linein off
;  bit 1: 0=output on, 1=output off
;  bit 2: 0=micin off, 1=micin on
proc    U_ChangeInOut
        mov     dx,[cs:Base]
        out     dx,al
        ret
endp    U_ChangeInOut

; The only reason I do it this way is because I haven't figured out the dump
; RAM to DRAM via DMA yet.
;
; Dump sample to Ram
;   ES:BX - Max 64k sample to dump to RAM.
;   SI:DI - DRAM location to dump to.
;   CX    - Max bytes to dump.
;
; Approximate time to dump 1 megabyte of samples to DRAM (assuming they were
; all loaded in memory) is 5 seconds.
proc    U_DumpSampleToDRAM
        push    cx
	mov	dx,[cs:u_Command]
        mov     al,44h          ; Dump upper byte, only do it on carry from now
        out     dx,al           ; on.
        add     dx,2
        push    ax
        mov     ax,si
        out     dx,al
        pop     ax
        sub     dx,2
@@MainLoop:
        mov     al,43h
        out     dx,al
        inc     dx
        push    ax
        mov     ax,di
        out     dx,ax
        pop     ax
        dec     dx
        add     di,1
        jnc     @@DumpByte
        inc     si
        mov     al,44h
        out     dx,al
        add     dx,2
        push    ax
        mov     ax,si
        out     dx,al
        pop     ax
        sub     dx,2
@@DumpByte:
        add     dx,4
        mov     al,[es:bx]
        xor     al,ah
        inc     bx
        out     dx,al
        sub     dx,4
        loop    @@MainLoop
	pop	cx
        ret
endp    U_DumpSampleToDRAM

u_Voice 	dw	0
u_Command	dw	0
u_DataLo	dw	0
u_DataHi	dw	0

; Carry set - No GUS
; No carry  - GUS at Base
proc    U_DetectGUS
        mov     [Word cs:Base],210h
@@TestIt:
	mov	dx,[cs:Base]
        add     dx,102h
        mov     [cs:u_Voice],dx
        inc     dx
        mov     [cs:u_Command],dx
        inc     dx
        mov     [cs:u_DataLo],dx
        inc     dx
        mov     [cs:u_DataHi],dx
        call    U_Probe
        jnb     @@FoundIt
        add     [Word cs:Base],10h
        cmp     [Word cs:Base],270h
        jb      @@TestIt
        stc
@@FoundIt:
	ret
endp    U_DetectGUS

;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
; s_PlayMusic - Make proper calls and interrupt sets for module playing.
;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²

proc		sd_SetMaxVolume
		push	ds
		mov	ax,cs
		mov	ds,ax
		mov	al,200		; Master volume scale value
		mov	[Byte Channel1+(offset (MS).MasterVolume)],al
		mov	[Byte Channel2+(offset (MS).MasterVolume)],al
		mov	[Byte Channel3+(offset (MS).MasterVolume)],al
		mov	[Byte Channel4+(offset (MS).MasterVolume)],al
		pop	ds
		ret
endp		sd_SetMaxVolume

proc            s_PlayMusic     far
		pusha
		push	ds
                mov     [word cs:mt_Speed],6
                mov     [word cs:mt_Counter],1
		mov	[word cs:mt_SongPos],0
		mov	[Word cs:mt_PatternPos],0
		call	sd_SetMaxVolume
;
;               Setup playing interrup routine
;
		mov	ax,0
		mov	ds,ax
		mov	bx,8*4
		mov	ax,[ds:bx]
		mov	[Word cs:IntStore],ax
		mov	ax,[ds:bx+2]
		mov	[Word cs:IntStore+2],ax
		cli
		mov	[Word ds:bx],offset PlayInt
		mov	[ds:bx+2],cs
		mov	ax,cs
		mov	ds,ax
		mov	al,36h
                out     43h,al                  ; timer program
		mov	ax,1193180/50
		out	40h,al
		mov	al,ah
		out	40h,al
		sti
@@End:		pop	ds
		popa
		ret
endp		s_PlayMusic
  
  
;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
; s_StopMusic - Shuts down the interrupts for generating music.
;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
  
proc		s_StopMusic	far
		pusha
		push	ds
		cli
		mov	ax,0
		mov	ds,ax
		mov	bx,8*4
		mov	ax,[Word cs:IntStore]
		mov	[ds:bx],ax
		mov	ax,[Word cs:IntStore+2]
		mov	[ds:bx+2],ax
		mov	al,36h
		out	43h,al
		xor	al,al
		out	40h,al
		out	40h,al
		sti
		pop	ds
		popa
		ret
endp		s_StopMusic
  
; CX - Sample Size
; SI:DI - Place in DRAM
; BX - Handle
LDSeg		dw	0
proc    LoadDumpSample
        push    cx bx
        mov     ah,48h
        mov     bx,cx
        shr     bx,4
        inc     bx
        int     21h
        mov     [cs:LDSeg],ax
        pop     bx
        push    ds
        mov     ah,3Fh
        mov     dx,0
        mov     ds,[cs:LDSeg]
        int     21h
        pop     ds

        pop     cx
        mov     es,[cs:LDSeg]
        mov     bx,0
        mov     ah,0
        call    U_DumpSampleToDRAM
        mov     ah,49h
        mov     es,[cs:LDSeg]
        int     21h
        ret
endp    LoadDumpSample

;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
; s_LoadModule - Load in the module pointed to by DS:DX.
;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
Handle		dw	0
proc		s_LoadModule
		pusha
		push	ds es
		mov	ax,3D00h
		int	21h
		mov	[cs:Handle],ax
		mov	ax,cs
                mov     ds,ax
                mov     es,ax
                call    s_ReadMod
		mov	ax,3E00h
		mov	bx,[cs:Handle]
		int	21h
		pop	es ds
		popa
		ret
endp		s_LoadModule

;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
; s_ReadMod - Read the header, all patterns, and all samples from module.
;²ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ²
LocInList	dw	?
proc		s_ReadMod
		push	ds
		mov	ax,3F00h		; Read the header
		mov	bx,[cs:Handle]
		mov	dx,offset Header
		mov	cx,HeaderSize
		int	21h

		call	s_LoadPatterns

		mov	cx,31
		mov	bx,offset samples+(offset (SampleRec).Length)
@@FlipLoop:
		mov	ax,[cs:bx]	; Flip length
		xchg	ah,al
		shl	ax,1
		mov	[cs:bx],ax
		mov	ax,[cs:bx+4]	; Flip repeat
		xchg	ah,al
		shl	ax,1
		mov	[cs:bx+4],ax
		mov	ax,[cs:bx+6]	; Flip repeat length
		xchg	ah,al
		shl	ax,1
		mov	[cs:bx+6],ax
		add	bx,size SampleRec
		loop	@@FlipLoop

		mov	cx,31
		mov	bx,offset samples+(offset (SampleRec).Length)
		mov	[Word cs:LocInList],0
		mov	si,0		; Location in DRAM to begin
		mov	di,0
@@DoSamples:	push	cx
		mov	ax,[cs:bx]
		or	ax,ax
		jz	@@ZeroByteSample
		mov	cx,ax		; Save for read from disk.
		push	bx
		mov	bx,[cs:LocInList]
		mov	[Word cs:bx+InsLoc],di
		mov	[Word cs:bx+InsLoc+2],si
		add	[Word cs:LocInList],4
		mov	bx,[cs:Handle]
		call	LoadDumpSample
		pop	bx
		jmp	@@Bottom
@@ZeroByteSample:
		push	bx
		mov	bx,[cs:LocInList]
		mov	[Word cs:bx+InsLoc],0
		mov	[Word cs:bx+InsLoc+2],0f000h
		add	[Word cs:LocInList],4
		pop	bx
@@Bottom:	add	bx,size SampleRec
		pop	cx
		loop	@@DoSamples
		pop	ds
		ret
endp		s_ReadMod

proc		s_GetHighestBlock
                mov     si,offset sequences
                mov     cx,128
                xor     ax,ax
@@SetBlock:	mov	ah,al
                jmp     @@BotLoop
@@SearchLoop:	lodsb
                cmp     al,ah
		jg	@@SetBlock
@@BotLoop:	loop	@@SearchLoop
                mov     al,ah
		inc	al
                xor     ah,ah                   ; Clear ah.
                ret
endp            s_GetHighestBlock

proc            s_LoadPatterns
		mov	cx,64
		mov	di,offset PatternLoc
		xor	ax,ax
		rep	stosw

		call	s_GetHighestBlock
		mov	[cs:NumPatterns],ax
		mov	di,offset PatternLoc
		mov	cx,ax
@@BlockAllocLoop:
		mov	bx,1024/4+1
		mov	ah,48h
		int	21h
		stosw
		loop	@@BlockAllocLoop

		mov	si,offset PatternLoc
		xor	dx,dx			; Load all segments at 0 offset.
		xor	cx,cx
@@BlockReadLoop:
		push	cx
		push	ds
		mov	cx,1024
		mov	bx,[cs:Handle]
		lodsw
		mov	ds,ax
		mov	ax,3F00h		; Load in the block.
		int	21h
		pop	ds cx
		inc	cx
		cmp	cx,[cs:NumPatterns]
		jnz	@@BlockReadLoop
		clc
		ret
endp		s_LoadPatterns

proc		s_CloseAllMusic
		push	ds
		mov	ax,cs
		mov	ds,ax
@@FreePatterns: mov     si,offset PatternLoc
		mov	cx,64
@@FreePatLoop:	lodsw
		or	ax,ax
		jz	@@BotFreePatLoop
		mov	es,ax
		mov	ax,4900h
		int	21h
		jnb	@@BotFreePatLoop
		stc
		pop	ds
		ret
@@BotFreePatLoop:
		loop	@@FreePatLoop
		pop	ds
		ret
endp		s_CloseAllMusic

proc            PlayInt
		pusha
		push	ds es
		call	sd_UpdateChannels
		sti
                mov     al,20h
                out     20h,al
                pop     es ds
		popa
		iret
endp            PlayInt

;ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
;³³³ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Protracker Stuff ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ³³³
;ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
;ÚÄ¿ Protracker specific
;ÀÄÙ variables.
mt_speed		db	6
mt_counter		db	0
mt_PatternPos		dw	0
mt_SongPos		db	0
mt_PattDelayTime2	db	0
mt_PattDelayTime	db	0
mt_PBreakFlag		db	0
mt_PBreakPos		db	0
mt_PosJumpFlag		db	0
mt_LowMask		db	0FFh

mt_FunkTable		db	0,5,6,7,8,10,11,13,16,19,22,26,32,43,64,128

mt_VibratoTable:	db	  0, 24, 49, 74, 97,120,141,161
			db	180,197,212,224,235,244,250,253
			db	255,253,250,244,235,224,212,197
			db	180,161,141,120, 97, 74, 49, 24

Channel1  MS	  <0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel2  MS	  <0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel3  MS	  <0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
Channel4  MS	  <0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>
PlayStatus	db	0
MStatus 	db	0
MainFreq	dd	0
Mode		db	0
SOffsetSet	db	0

mt_VolTable	dw	00000h,0A000h,0A800h,0B000h,0B800h,0C000h,0C400h,0C800h,0CC00h
		dw	0D000h,0D200h,0D400h,0D600h,0D800h,0DA00h,0DC00h,0DE00h
		dw	0E000h,0E100h,0E200h,0E300h,0E400h,0E500h,0E600h,0E700h
		dw	0E800h,0E900h,0EA00h,0EB00h,0EC00h,0ED00h,0EE00h,0EF00h
		dw	0F080h,0F100h,0F180h,0F200h,0F280h,0F300h,0F380h,0F400h
		dw	0F480h,0F500h,0F580h,0F600h,0F680h,0F700h,0F780h,0F800h
		dw	0F880h,0F900h,0F980h,0FA00h,0FA80h,0FB00h,0FB80h,0FC00h
		dw	0FC80h,0FD00h,0FD80h,0FE00h,0FE80h,0FF00h,0FF80h,0FFFFh

mt_PeriodTable:
; Tuning 0, Normal
	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,0
; Tuning 1
	dw    850,802,757,715,674,637,601,567,535,505,477,450
	dw    425,401,379,357,337,318,300,284,268,253,239,225
	dw    213,201,189,179,169,159,150,142,134,126,119,113,0
; Tuning 2
	dw    844,796,752,709,670,632,597,563,532,502,474,447
	dw    422,398,376,355,335,316,298,282,266,251,237,224
	dw    211,199,188,177,167,158,149,141,133,125,118,112,0
; Tuning 3
	dw    838,791,746,704,665,628,592,559,528,498,470,444
	dw    419,395,373,352,332,314,296,280,264,249,235,222
	dw    209,198,187,176,166,157,148,140,132,125,118,111,0
; Tuning 4
	dw    832,785,741,699,660,623,588,555,524,495,467,441
	dw    416,392,370,350,330,312,294,278,262,247,233,220
	dw    208,196,185,175,165,156,147,139,131,124,117,110,0
; Tuning 5
	dw    826,779,736,694,655,619,584,551,520,491,463,437
	dw    413,390,368,347,328,309,292,276,260,245,232,219
	dw    206,195,184,174,164,155,146,138,130,123,116,109,0
; Tuning 6
	dw    820,774,730,689,651,614,580,547,516,487,460,434
	dw    410,387,365,345,325,307,290,274,258,244,230,217
	dw    205,193,183,172,163,154,145,137,129,122,115,109,0
; Tuning 7
	dw    814,768,725,684,646,610,575,543,513,484,457,431
	dw    407,384,363,342,323,305,288,272,256,242,228,216
	dw    204,192,181,171,161,152,144,136,128,121,114,108,0
; Tuning -8
	dw    907,856,808,762,720,678,640,604,570,538,508,480
	dw    453,428,404,381,360,339,320,302,285,269,254,240
	dw    226,214,202,190,180,170,160,151,143,135,127,120,0
; Tuning -7
	dw    900,850,802,757,715,675,636,601,567,535,505,477
	dw    450,425,401,379,357,337,318,300,284,268,253,238
	dw    225,212,200,189,179,169,159,150,142,134,126,119,0
; Tuning -6
	dw    894,844,796,752,709,670,632,597,563,532,502,474
	dw    447,422,398,376,355,335,316,298,282,266,251,237
	dw    223,211,199,188,177,167,158,149,141,133,125,118,0
; Tuning -5
	dw    887,838,791,746,704,665,628,592,559,528,498,470
	dw    444,419,395,373,352,332,314,296,280,264,249,235
	dw    222,209,198,187,176,166,157,148,140,132,125,118,0
; Tuning -4
	dw    881,832,785,741,699,660,623,588,555,524,494,467
	dw    441,416,392,370,350,330,312,294,278,262,247,233
	dw    220,208,196,185,175,165,156,147,139,131,123,117,0
; Tuning -3
	dw    875,826,779,736,694,655,619,584,551,520,491,463
	dw    437,413,390,368,347,328,309,292,276,260,245,232
	dw    219,206,195,184,174,164,155,146,138,130,123,116,0
; Tuning -2
	dw    868,820,774,730,689,651,614,580,547,516,487,460
	dw    434,410,387,365,345,325,307,290,274,258,244,230
	dw    217,205,193,183,172,163,154,145,137,129,122,115,0
; Tuning -1
	dw    862,814,768,725,684,646,610,575,543,513,484,457
	dw    431,407,384,363,342,323,305,288,272,256,242,228
	dw    216,203,192,181,171,161,152,144,136,128,121,114,0
;
;
include "ptable.inc"

macro	SetVoice V
        mov     [Byte cs:Voice],V
        mov     dx,[cs:u_Voice]
        mov     al,V
        out     dx,al
endm    SetVoice

Voice   db      0


;ÕÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¸
;³ NAME       : sd_UpdateChannels                                            ³
;³              *** Original code: Amiga Protracker by Lars "Zap" Hamre.     ³
;³              *** Converted by Joshua C. Jensen.                           ³
;ÔÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¾
proc	sd_UpdateChannels near
	mov	ax,cs
	mov	ds,ax
	mov	es,ax

mt_ContinueUpdate:
	inc	[Byte cs:mt_counter]
	mov	al,[cs:mt_counter]
	cmp	al,[cs:mt_speed]
	jb	mt_NoNewNote
	mov	[Byte cs:mt_counter],0
	cmp	[Byte cs:mt_PattDelayTime2],0
	jz	mt_GetNewNote
	call	mt_NoNewAllChannels
	jmp	mt_dskip

mt_NoNewNote:
	call	mt_NoNewAllChannels
	jmp	mt_NoNewPosYet

mt_NoNewAllChannels:
	SetVoice 1
        mov     si,offset Channel1
	call	mt_CheckEfx
	SetVoice 2
	mov	si,offset Channel2
        call    mt_CheckEfx
	SetVoice 3
	mov	si,offset Channel3
	call	mt_CheckEfx
	SetVoice 4
	mov	si,offset Channel4
	jmp	mt_CheckEfx

mt_GetNewNote:
	xor	ax,ax
	mov	al,[cs:mt_SongPos]
	mov	di,ax
	mov	al,[cs:di+sequences]
	shl	al,1
	mov	di,offset PatternLoc
	add	di,ax
	mov	es,[cs:di]
	mov	di,[cs:mt_PatternPos]


	SetVoice 1
	mov	si,offset Channel1
	call	mt_PlayVoice
	SetVoice 2
	mov	si,offset Channel2
	call	mt_PlayVoice
	SetVoice 3
	mov	si,offset Channel3
	call	mt_PlayVoice
	SetVoice 4
	mov	si,offset Channel4
        call    mt_PlayVoice

	jmp	mt_dskip

mt_PlayVoice:
	mov	[Byte cs:Mode],0
        cmp     [Word cs:si],0
	jnz	mt_plvskip
	cmp	[Word cs:si+2],0
	jnz	mt_plvskip
	call	mt_PerNop
mt_plvskip:
	mov	ax,[es:di]
	mov	[cs:si+MS.Note],ax
	mov	ax,[es:di+02h]
	mov	[Word cs:si+MS.cmd],ax
	add	di,4

	mov	al,[cs:si+MS.cmd]
	and	al,0F0h
	shr	al,4
	mov	ah,[Byte cs:si+MS.Note]
	and	ah,0F0h
	or	al,ah			; Is there an ins?
	jz	mt_SetRegisters
	dec	al
        mov     bl,al
;	 mov	 [cs:bp+CD.sins],al
	mov	[cs:si+MS.SampleNum],al
	mov	bh,size SampleRec
	mul	bh
	mov	dx,ax

	xor	bh,bh
	shl	bx,2
	add	bx,offset InsLoc
	mov	ax,[cs:bx]
	mov	[Word cs:si+MS.start],ax
	mov	ax,[cs:bx+2]
	mov	[Word cs:si+(offset (MS).start)+2],ax
        mov     bx,dx
	add	bx,(offset (SampleRec).Length)
	add	bx,offset samples
	mov	cx,[Word cs:bx]
	sub	cx,3
	mov	ax,[Word cs:si+MS.start]
	mov	dx,[Word cs:si+(offset (MS).start)+2]
	add	ax,cx
        adc     dx,0
        mov     [Word cs:si+MS.Length],ax
	mov	[Word cs:si+(offset (MS).Length)+2],dx
	mov	[Word cs:si+MS.RealLength],cx
	mov	ax,[Word cs:bx+2]
	mov	[Word cs:si+MS.FineTune],ax
	mov	al,ah
	call	volequ

	mov	cx,[Word cs:bx+6]; Get the Repeat length.
	cmp	cx,2
	jbe	mt_NoLoop
	mov	cx,[Word cs:bx+4]
	or	[Byte cs:Mode],00001000b
	mov	ax,[Word cs:si+MS.start]
	mov	dx,[Word cs:si+(offset (MS).start)+2]
	add	ax,cx
	adc	dx,0
	mov	[Word cs:si+MS.loopstart],ax
	mov	[Word cs:si+(offset (MS).loopstart)+2],dx
	mov	[Word cs:si+MS.wavestart],ax
	mov	[Word cs:si+(offset (MS).wavestart)+2],dx
	mov	ax,[Word cs:bx+6]
	mov	[Word cs:si+MS.RepLen],ax
	jmp	mt_SetRegisters
mt_NoLoop:
	mov	ax,[Word cs:si+MS.start]
	mov	dx,[Word cs:si+(offset (MS).start)+2]
	mov	[Word cs:si+MS.loopstart],ax
	mov	[Word cs:si+(offset (MS).loopstart)+2],dx
	mov	[Word cs:si+MS.wavestart],ax
	mov	[Word cs:si+(offset (MS).wavestart)+2],dx
	mov	ax,[Word cs:bx]
        mov     [Word cs:si+MS.RepLen],ax
mt_SetRegisters:
	mov	ax,[cs:si+MS.Note]
	xchg	ah,al
	and	ax,0FFFh
	jz	mt_CheckMoreEfx
	mov	ax,[Word cs:si+MS.cmd]
	xchg	ah,al
	and	ax,0FF0h
	cmp	ax,0E50h
	jz	mt_DoSetFineTune
	cmp	ah,3			; Is it a tone portamento?
	jz	mt_ChkTonePorta
	cmp	ah,5			; Is it a tone and volume slide?
	jz	mt_ChkTonePorta
	cmp	ah,9			; Is it a sample offset command?
	jnz	mt_SetPeriod
	call	mt_CheckMoreEfx
	jmp	mt_SetPeriod

mt_DoSetFineTune:
	call	mt_SetFineTune
	jmp	mt_SetPeriod

mt_ChkTonePorta:
	call	mt_SetTonePorta
	jmp	mt_CheckMoreEfx

mt_SetPeriod:
	mov	ax,[cs:si+MS.Note]
	xchg	ah,al
	and	ax,0FFFh
	mov	bx,offset mt_PeriodTable
	mov	cx,36
mt_ftuloop:
	cmp	ax,[cs:bx]
	jnb	mt_ftufound
	add	bx,2
	loop	mt_ftuloop
mt_ftufound:
	mov	al,[cs:si+MS.FineTune]
	mov	cl,37*2
	mul	cl
	add	bx,ax
	mov	ax,[cs:bx]
	mov	[cs:si+MS.Period],ax

	mov	ax,[Word cs:si+MS.cmd]
	xchg	ah,al
	and	ax,0FF0h
	cmp	ax,0ED0h
	jz	mt_CheckMoreEfx

	mov	al,[cs:si+MS.WaveControl]  ; Get the WaveControl.
	and	al,00000100b		; Amiga: BTST #2,WaveControl.
	jz	mt_vibnoc		; If it is zero, then skip.
	mov	[Byte cs:si+MS.VibratoPos],0
mt_vibnoc:
	mov	al,[cs:si+MS.WaveControl]  ; Get it again.
	and	al,01000000b		; Amiga: BTST #6,WaveControl
	jz	mt_trenoc		; If it is zero, then skip.
	mov	[Byte cs:si+MS.TremoloPos],0 ; Zero the Tremolo offset.
mt_trenoc:
        ; high byte of sample start == F000H, turn off the voice
	cmp	[Word cs:si+(offset (MS).start)+2],0f000h
        jz      @@NoVoice
	cmp	[Byte cs:SOffsetSet],1
	jz	@@SkipBegin
	mov	dx,[cs:u_Command]
	mov	al,0
	out	dx,al
	call	UDelay
	mov	al,0
	out	dx,al
	add	dx,2
	mov	al,[Byte cs:Mode]
	or	al,3
	out	dx,al
        ; Send sample begin
	mov	dx,[cs:u_Command]
        mov     al,0ah
        out     dx,al
        inc     dx              ; 104h
	mov	ax,[Word cs:si+MS.start]
	mov	cx,[Word cs:si+(offset (MS).start)+2]
	call	RShift
        out     dx,ax
        dec     dx              ; 103h
        mov     al,0bh
        out     dx,al
        inc     dx              ; 104h
	mov	ax,[Word cs:si+MS.start]
        shl     ax,9
        out     dx,ax

@@SkipBegin:
	mov	[Byte cs:SOffsetSet],0
	mov	dx,[cs:u_Command]
        mov     al,2
	out	dx,al
	inc	dx		; 104h
	mov	ax,[Word cs:si+MS.loopstart]
	mov	cx,[Word cs:si+(offset (MS).loopstart)+2]
	call	RShift
	out	dx,ax
	dec	dx		; 103h
	mov	al,3
	out	dx,al
	inc	dx		; 104h
	mov	ax,[Word cs:si+MS.loopstart]
	shl	ax,9
	out	dx,ax

	mov	dx,[cs:u_Command]
        mov     al,4
	out	dx,al
	inc	dx		; 104h
	mov	ax,[Word cs:si+MS.length]
	mov	cx,[Word cs:si+(offset (MS).length)+2]
	call	RShift
	out	dx,ax
	dec	dx		; 103h
	mov	al,5
	out	dx,al
	inc	dx		; 104h
	mov	ax,[Word cs:si+MS.length]
	shl	ax,9
	out	dx,ax
	dec	dx		; 103h

	mov	cx,[cs:si+MS.Period]
	call	mt_PerNop
;	 mov	 al,[Byte cs:si+MS.Volume]
;	 call	 volequ
	mov	dx,[cs:u_Command]
	mov	al,0ch
	out	dx,al
	add	dx,2
	mov	al,0		 ;	 channel 1 pan
	cmp	[cs:Voice],1
	jz	@@C3
	mov	al,15		; channel 2 pan
	cmp	[cs:Voice],2
	jz	@@C3
	mov	al,12		; channel 3 pan
	cmp	[cs:Voice],3
	jz	@@C3
	mov	al,3		; channel 4 pan
@@C3:
	out	dx,al
	sub	dx,2
	mov	al,0
        out     dx,al
	call	UDelay
	mov	al,0
	out	dx,al
	add	dx,2
	mov	al,[Byte cs:Mode]
        out     dx,al
	jmp	@@Bottom

;
;       Shuts off the voice
;
@@NoVoice:
	mov	[Byte cs:SOffsetSet],0
	mov	al,0
	out	dx,al
	add	dx,2
	mov	al,[Byte cs:Mode]
	or	al,00000011b
	out	dx,al
@@Bottom:
@@Done:
;	 call	 SpectrumAnalyzer
	mov	[cs:si+MS.Trigger],1
	jmp	mt_CheckMoreEfx
endp	sd_UpdateChannels

proc	mt_dskip near
        add     [Word cs:mt_PatternPos],16
	mov	al,[cs:mt_PattDelayTime]
	or	al,al
	jz	mt_dskc
	mov	[cs:mt_PattDelayTime2],al
	mov	[Byte cs:mt_PattDelayTime],0
mt_dskc:
	cmp	[Byte cs:mt_PattDelayTime2],0
	jz	mt_dska
	dec	[Byte cs:mt_PattDelayTime2]
	jz	mt_dska
	sub	[Word cs:mt_PatternPos],16
mt_dska:
	cmp	[Byte cs:mt_PBreakFlag],0
	jz	mt_nnpysk
	mov	[Byte cs:mt_PBreakFlag],0
	xor	ax,ax
	mov	al,[cs:mt_PBreakPos]
	mov	[cs:mt_PBreakPos],ah
	shl	ax,4
	mov	[cs:mt_PatternPos],ax
mt_nnpysk:
	cmp	[Word cs:mt_PatternPos],1024
	jb	mt_NoNewPosYet

mt_NextPosition:
	xor	ax,ax
	mov	al,[cs:mt_PBreakPos]
	shl	ax,4
	mov	[cs:mt_PatternPos],ax
	mov	[Byte cs:mt_PBreakPos],0
	mov	[Byte cs:mt_PosJumpFlag],0
	inc	[Byte cs:mt_SongPos]
	and	[Byte cs:mt_SongPos],7Fh
	mov	al,[cs:mt_SongPos]
	cmp	al,[cs:songlen]
	jnz	mt_NoNewPosYet
	mov	[Byte cs:mt_SongPos],0
	cmp	[Byte cs:PlayStatus],1
	jnz	@@Quit
	mov	[Byte cs:mt_SongPos],0
	ret
;---------------------------------------------------
@@Quit: mov	[Byte cs:MStatus],1
	mov	[Byte cs:mt_SongPos],0
	ret

mt_NoNewPosYet:
	cmp	[Byte cs:mt_PosJumpFlag],0
	jnz	mt_NextPosition
	ret
endp	mt_dskip

proc	mt_CheckEfx near
	call	mt_UpdateFunk
	mov	ax,[Word cs:si+MS.cmd]		    ; Get the special command
	xchg	al,ah
	and	ax,0FFFh
	jz	mt_Return			;mt_Return
	mov	bl,[cs:si+MS.cmd]	       ; Contains the command to do
	and	bl,0Fh
	cmp	bl,0
	jz	mt_Arpeggio
	cmp	bl,1
	jz	mt_PortaUp
	cmp	bl,2
	jz	mt_PortaDown
	cmp	bl,3
	jz	mt_TonePortamento
	cmp	bl,4
	jz	mt_Vibrato
	cmp	bl,5
	jz	mt_TonePlusVolSlide
	cmp	bl,6
	jz	mt_VibratoPlusVolSlide
	cmp	bl,0Eh
	jz	mt_E_Commands
SetBack:call	mt_PerNop
	cmp	bl,7
	jz	mt_Tremolo
	cmp	bl,0Ah
	jz	mt_VolumeSlide
mt_Return:
	ret
endp	mt_CheckEfx

proc	mt_PerNop near
        mov     ax,[cs:si+MS.Period]    ; get Period Value
proc	mt_PerNop2 near
	and	ax,0FFFh
	or	ax,ax
	jz	@@Outit
        push    bx                      ; ????
        push    di
        mov     bx,ax
        sub     bx,100                  ; bx = bx - 100
        shl     bx,1                    ; bx = bx * 2
        mov     di,Offset Ptable
        mov     ax,[cs:di+bx]
        pop     di
        pop     bx
;        mov     cx,ax
;        xor     dx,dx
;        mov     ax,8448
;        div     cx
;        mov     cx,428
;        mul     cx
;        mov     cx,19
;        div     cx
@@OutIt:
	push	ax
	mov	dx,[cs:u_Command]
        mov     al,1                    ; set frequency
	out	dx,al
	inc	dx
	pop	ax
        out     dx,ax
        ret
endp	mt_PerNop2
endp    mt_PerNop

; Effect 0 -- Arpeggio
proc	mt_Arpeggio near
	xor	ax,ax
	mov	al,[cs:mt_counter]
	mov	bl,3
	div	bl
	xchg	al,ah
	cmp	al,1
	jz	mt_Arpeggio1
	cmp	al,2
	jz	mt_Arpeggio2
mt_Arpeggio0:
	mov	cx,[cs:si+MS.period]
	jmp	ArpeggioSet

mt_Arpeggio1:
	xor	ax,ax
	mov	al,[Byte cs:si+MS.cmdlo]
	shr	al,4
	jmp	ArpeggioFind

mt_Arpeggio2:
	xor	ax,ax
	mov	al,[cs:si+MS.cmdlo]
	and	al,15
ArpeggioFind:
	shl	ax,1
	mov	bx,ax
	mov	al,[cs:si+MS.FineTune]
	mov	cl,37*2
	mul	cl
	mov	dx,[cs:si+MS.Period]
	push	di
	mov	di,offset mt_PeriodTable
	add	di,ax
	mov	cx,36
mt_arploop:
	mov	ax,[cs:bx+di]
	cmp	dx,[cs:di]
	jnb	mt_arpafterloop
	add	di,2
	loop	mt_arploop
	pop	di
	ret
mt_arpafterloop:
	pop	di
	mov	cx,ax

ArpeggioSet:
	call	mt_PerNop2
	ret
endp	mt_Arpeggio

; Effect 1 -- Portamento Up
proc	mt_PortaUp near
	xor	ax,ax
	mov	al,[cs:si+MS.cmdlo]		   ; Number to slide up
	and	al,[cs:mt_LowMask]
	mov	[Byte cs:mt_LowMask],0FFh
	sub	[cs:si+MS.Period],ax
	mov	cx,[cs:si+MS.Period]
	and	cx,0FFFh
	cmp	cx,71h
	jnb	mt_PortaUSkip
	and	[cs:si+MS.Period],0F000h
	or	[cs:si+MS.Period],71h
mt_PortaUSkip:
	mov	cx,[cs:si+MS.Period]
	and	cx,0FFFh
	call	mt_PerNop
	ret
endp	mt_PortaUp


; Effect 2 -- Portamento Down
proc	mt_PortaDown near
	xor	ax,ax
	mov	al,[cs:si+MS.cmdlo]		   ; Number to slide down
	and	al,[cs:mt_LowMask]
	mov	[Byte cs:mt_LowMask],0FFh
	add	[cs:si+MS.Period],ax
	mov	cx,[cs:si+MS.Period]
	and	cx,0FFFh
	cmp	cx,358h
	jb	mt_PortaDSkip
	and	[cs:si+MS.Period],0F000h
	or	[cs:si+MS.Period],856
mt_PortaDSkip:
	mov	cx,[cs:si+MS.Period]
	and	cx,0FFFh
	call	mt_PerNop
	ret
endp	mt_PortaDown

proc	mt_SetTonePorta near
	push	di
	mov	dx,[cs:si+MS.Note]
	xchg	dh,dl
	and	dx,0FFFh
	xor	ax,ax
	mov	al,[cs:si+MS.FineTune]
	mov	cl,37*2
	mul	cl
	mov	di,offset mt_PeriodTable
	add	di,ax
	mov	bx,0
mt_StpLoop:
	cmp	dx,[cs:bx+di]
	jnb	mt_StpFound
	add	bx,2
	cmp	bx,37*2
	jb	mt_StpLoop
	mov	bx,35*2
mt_StpFound:
	mov	dl,[cs:si+MS.FineTune]
	and	dl,8
	jz	mt_StpGoss
	or	bx,bx
	jz	mt_StpGoss
	sub	bx,2
mt_StpGoss:
	mov	dx,[cs:bx+di]
	pop	di
	mov	[cs:si+MS.WantedPeriod],dx
	mov	ax,[cs:si+MS.Period]
	mov	[Byte cs:si+MS.TonePortDirec],0
	cmp	dx,ax
	jz	mt_ClearTonePorta
	jnb	mt_Return
	mov	[Byte cs:si+MS.TonePortDirec],1
	ret

mt_ClearTonePorta:
	mov	[Word cs:si+MS.WantedPeriod],0
	ret
endp	mt_SetTonePorta


; Effect 3 -- Tone Portamento
proc	mt_TonePortamento near
	mov	al,[cs:si+MS.cmdlo]
	or	al,al
	jz	mt_TonePortNoChange
	mov	[cs:si+MS.TonePortSpeed],al
	mov	[Byte cs:si+MS.cmdlo],0
mt_TonePortNoChange:
	cmp	[Word cs:si+MS.WantedPeriod],0
	jz	mt_Return
	xor	ax,ax
	mov	al,[cs:si+MS.TonePortSpeed]
	cmp	[Byte cs:si+MS.TonePortDirec],0
	jnz	mt_TonePortaUp
mt_TonePortaDown:
	add	[cs:si+MS.Period],ax
	mov	ax,[cs:si+MS.WantedPeriod]
	cmp	ax,[cs:si+MS.Period]
	jg	mt_TonePortaSetPer
	mov	[cs:si+MS.Period],ax
	mov	[Word cs:si+MS.WantedPeriod],0
	jmp	mt_TonePortaSetPer

mt_TonePortaUp:
	sub	[cs:si+MS.Period],ax
	mov	ax,[cs:si+MS.WantedPeriod]
	cmp	ax,[cs:si+MS.Period]
	jl	mt_TonePortaSetPer
	mov	[cs:si+MS.Period],ax
	mov	[Word cs:si+MS.WantedPeriod],0

mt_TonePortaSetPer:
	mov	cx,[cs:si+MS.Period]
	mov	al,[cs:si+MS.GlissFunk]
	and	al,0Fh
	jz	mt_GlissSkip
	mov	al,[cs:si+MS.FineTune]
	mov	bl,37*2
	mul	bl
	push	di
	mov	di,offset mt_PeriodTable
	add	di,ax
	mov	bx,0
mt_GlissLoop:
	cmp	cx,[cs:bx+di]
	jnb	mt_GlissFound
	add	bx,2
	cmp	bx,37*2
	jb	mt_GlissLoop
	mov	bx,35*2
mt_GlissFound:
	mov	cx,[cs:bx+di]
	pop	di
mt_GlissSkip:
	call	mt_PerNop
	ret
endp	mt_TonePortamento


; Effect 4 -- Vibrato
proc	mt_Vibrato near
	mov	al,[cs:si+MS.cmdlo]
	or	al,al
	jz	mt_Vibrato2
	mov	cl,[cs:si+MS.VibratoCmd]
	and	al,0fh
	jz	mt_vibskip
	and	cl,0F0h
	or	cl,al
mt_vibskip:
	mov	al,[cs:si+MS.cmdlo]
	and	al,0F0h
	jz	mt_vibskip2
	and	cl,0Fh
	or	cl,al
mt_vibskip2:
	mov	[cs:si+MS.VibratoCmd],cl
mt_Vibrato2:
	mov	al,[cs:si+MS.VibratoPos]
	mov	bx,offset mt_VibratoTable
	shr	ax,2
	and	ax,001Fh
	xor	cx,cx
	mov	cl,[cs:si+MS.WaveControl]
	and	cl,03h
	jz	mt_vib_sine
	shl	al,3
	cmp	cl,1
	jz	mt_vib_rampdown
	mov	cl,255
	jmp	mt_vib_set
mt_vib_rampdown:
	cmp	[Byte cs:si+MS.VibratoPos],0
	jnb	mt_vib_rampdown2
	mov	cl,255
	sub	cl,al
	jmp	mt_vib_set
mt_vib_rampdown2:
	mov	cl,al
	jmp	mt_vib_set
mt_vib_sine:
	add	bl,al
	mov	cl,[cs:bx]
mt_vib_set:
	mov	al,[cs:si+MS.VibratoCmd]
	and	al,0Fh
	mul	cl
	shr	ax,7
	mov	cx,ax
	mov	ax,[cs:si+MS.Period]
	cmp	[Byte cs:si+MS.VibratoPos],0
	jb	mt_VibratoNeg		; BMI
	add	ax,cx
	jmp	mt_Vibrato3
mt_VibratoNeg:
	sub	ax,cx
mt_Vibrato3:
	mov	cx,ax
	call	mt_PerNop2
	mov	al,[cs:si+MS.VibratoCmd]
	shr	ax,2
	and	ax,3Ch
	add	[cs:si+MS.VibratoPos],al
	ret
endp	mt_Vibrato


; Effect 5 -- Tone and Volume Slide
proc	mt_TonePlusVolSlide near
	call	mt_TonePortNoChange
	jmp	mt_VolumeSlide
endp	mt_TonePlusVolSlide

; Effect 6 -- Vibrato and Volume Slide
proc	mt_VibratoPlusVolSlide near
	call	mt_Vibrato2
	jmp	mt_VolumeSlide
endp	mt_VibratoPlusVolSlide


; Effect 7 -- Tremolo
proc	mt_Tremolo near
	mov	al,[Byte cs:si+MS.cmdlo]
	or	al,al
	jz	mt_Tremolo2
	mov	cl,[cs:si+MS.TremoloCmd]
	and	al,0Fh
	jz	mt_treskip
	and	cl,0F0h
	or	cl,al
mt_treskip:
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0F0h
	jz	mt_treskip2
	and	cl,0Fh
	or	cl,al
mt_treskip2:
	mov	[cs:si+MS.TremoloCmd],cl
mt_Tremolo2:
	mov	al,[cs:si+MS.TremoloPos]
	shr	al,2
	and	ax,001Fh
	xor	cx,cx
	mov	cl,[cs:si+MS.WaveControl]
	shr	cl,4
	and	cl,03h
	jz	mt_tre_sine
	shl	al,3
	cmp	cl,1
	jz	mt_tre_rampdown
	mov	cl,255
	jmp	mt_tre_set
mt_tre_rampdown:
	cmp	[Byte cs:si+MS.VibratoPos],0
	jnb	mt_tre_rampdown2
	mov	cl,255
	sub	cl,al
	jmp	mt_tre_set
mt_tre_rampdown2:
	mov	cl,al
	jmp	mt_tre_set
mt_tre_sine:
	xor	bx,bx
	mov	bl,al
	mov	cl,[cs:bx+offset mt_VibratoTable]
mt_tre_set:
	mov	al,[cs:si+MS.TremoloCmd]
	and	al,0Fh
	mul	cl
	mov	cx,ax
	shr	cx,6
	mov	al,[cs:si+MS.Volume]
	cmp	[Byte cs:si+MS.TremoloPos],0
	jb	mt_TremoloNeg		; BMI  jns
	add	al,cl
	jmp	mt_Tremolo3
mt_TremoloNeg:
	sub	al,cl
mt_Tremolo3:
	jnb	mt_TremoloSkip
	xor	ax,ax
mt_TremoloSkip:
	cmp	al,40h
	jb	mt_TremoloOK		; BLS
	mov	al,40h
mt_TremoloOK:
	call	volequ
	mov	al,[cs:si+MS.TremoloCmd]
	shr	al,2
	and	al,3Ch
	add	[cs:si+MS.TremoloPos],al
	ret
endp	mt_Tremolo

TempHi	dw	0
TempLo	dw	0

; Effect 9 -- Sample Offset
proc	mt_SampleOffset near
	xor	ax,ax
	mov	al,[Byte cs:si+MS.cmdlo]
	or	al,al
	jz	mt_sononew
	mov	[cs:si+MS.SampleOffset],al
mt_sononew:
	mov	al,[cs:si+MS.SampleOffset]	   ; Variance in Protracker code.
	shl	ax,8
;        cmp     ax,[cs:si+MS.Reallength]
;	 jge	 mt_sofskip
	mov	cx,ax
	mov	ax,[Word cs:si+MS.start]
	mov	dx,[Word cs:si+(offset (MS).start)+2]
	add	ax,cx
        adc     dx,0
	mov	[Word cs:si+MS.start],ax
	mov	[Word cs:si+(offset (MS).start)+2],dx
	ret
mt_sofskip:
	ret
endp	mt_SampleOffset


; Effect A -- Volume Slide
proc	mt_VolumeSlide near
	mov	al,[cs:si+MS.cmdlo]
	shr	al,4
	or	al,al
	jz	mt_VolSlideDown
mt_VolSlideUp:
	add	[Byte cs:si+MS.Volume],al
	cmp	[Byte cs:si+MS.Volume],40h
	jb	mt_vsuskip
	mov	[Byte cs:si+MS.Volume],40h
mt_vsuskip:
	mov	al,[Byte cs:si+MS.Volume]
	call	volequ
	ret

mt_VolSlideDown:
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
mt_VolSlideDown2:
	sub	[Byte cs:si+MS.Volume],al
	jnb	mt_vsdskip
	mov	[Byte cs:si+MS.Volume],0
mt_vsdskip:
	mov	al,[Byte cs:si+MS.Volume]
	call	volequ
	ret
endp	mt_VolumeSlide


; Effect B -- Position Jump
proc	mt_PositionJump near
	mov	al,[Byte cs:si+MS.cmdlo]	 ; Get where to jump
	dec	al			      ; Update the
	mov	[cs:mt_SongPos],al		 ; information.
mt_pj2: mov	[Byte cs:mt_PBreakPos],0
	mov	[Byte cs:mt_PosJumpFlag],1
	ret
endp	mt_PositionJump

; Effect C -- Volume Change
proc	mt_VolumeChange near
	mov	al,[Byte cs:si+MS.cmdlo]   ; Get value for volume
	cmp	al,40h			; Is it greater than 40h?
	jb	mt_VolumeOK		; Nope
	mov	al,40h
mt_VolumeOK:
	mov	[cs:si+MS.Volume],al	   ; Get it again
	call	volequ
	ret
endp	mt_VolumeChange

; Effect D -- Pattern Break
proc	mt_PatternBreak near
	mov	al,[Byte cs:si+MS.cmdlo]	     ; Break to where?
	mov	cl,al
	shr	al,4
	mov	bl,10
	mul	bl
	and	cl,0Fh
	add	al,cl
	cmp	al,63
	jg	mt_pj2
	mov	[cs:mt_PBreakPos],al
	mov	[Byte cs:mt_PosJumpFlag],1
	ret
endp	mt_PatternBreak


; Effect F -- Set Speed
;   Doesn't handle Protracker extended speeds.
proc	mt_SetSpeed near
	mov	al,[Byte cs:si+MS.cmdlo]	     ; Get value for speed
	or	al,al
	jz	@@SSpe1
	cmp	al,32
	jnb	@@SSpe1
	mov	[Byte cs:mt_counter],0
	mov	[Byte cs:mt_speed],al
@@SSPe1:ret
endp	mt_SetSpeed


proc	mt_CheckMoreEfx near
	call	mt_UpdateFunk
	mov	bl,[cs:si+MS.cmd]
	and	bl,0Fh
	cmp	bl,09h
	jz	mt_SampleOffset
	cmp	bl,0Bh
	jz	mt_PositionJump
	cmp	bl,0Dh
	jz	mt_PatternBreak
	cmp	bl,0Eh
	jz	mt_E_Commands
	cmp	bl,0Fh
	jz	mt_SetSpeed
	cmp	bl,0Ch
	jz	mt_VolumeChange
	jmp	mt_PerNop
endp	mt_CheckMoreEfx

proc	mt_E_Commands near
	mov	bl,[Byte cs:si+MS.cmdlo]
	and	bl,0F0h
	shr	bl,4
	cmp	bl,0
	jz	mt_FilterOnOff
	cmp	bl,1
	jz	mt_FinePortaUp
	cmp	bl,2
	jz	mt_FinePortaDown
	cmp	bl,3
	jz	mt_SetGlissControl
	cmp	bl,4
	jz	mt_SetVibratoControl
	cmp	bl,5
	jz	mt_SetFineTune
	cmp	bl,6
	jz	mt_JumpLoop
	cmp	bl,7
	jz	mt_SetTremoloControl
	cmp	bl,8
	jz	mt_KarplusStrong
	cmp	bl,0Eh
	jz	mt_PatternDelay
	cmp	bl,9
	jz	mt_RetrigNote
	cmp	bl,0Ah
	jz	mt_VolumeFineUp
	cmp	bl,0Bh
	jz	mt_VolumeFineDown
	cmp	bl,0Ch
	jz	mt_NoteCut
	cmp	bl,0Dh
	jz	mt_NoteDelay
	cmp	bl,0Fh
	jz	mt_FunkIt
	ret
endp	mt_E_Commands

;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Effect E
;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Effect 0 -- FilterOnOff
proc	mt_FilterOnOff near
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,1
	sal	al,1
	; Amiga
	; Stuff
	ret
endp	mt_FilterOnOff

; Effect 1 -- Fine Porta Up
proc	mt_FinePortaUp near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	[Byte cs:mt_LowMask],0Fh
	jmp	mt_PortaUp
endp	mt_FinePortaUp

; Effect 2 -- Fine Porta Down
proc	mt_FinePortaDown near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	[Byte cs:mt_LowMask],0Fh
	jmp	mt_PortaDown
endp	mt_FinePortaDown

; Effect 3 -- Set Gliss Control
proc	mt_SetGlissControl near
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	and	[Byte cs:si+MS.GlissFunk],0F0h
	or	[cs:si+MS.GlissFunk],al
	ret
endp	mt_SetGlissControl

; Effect 4 -- Set Vibrato Control
proc	mt_SetVibratoControl near
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
	and	[Byte cs:si+MS.WaveControl],0F0h
	or	[cs:si+MS.WaveControl],al
	ret
endp	mt_SetVibratoControl

; Effect 5 -- Set Fine Tune
proc	mt_SetFineTune near
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
	mov	[cs:si+MS.FineTune],al
	ret
endp	mt_SetFineTune

; Effect 6 -- Jump Loop
proc	mt_JumpLoop near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
	jz	mt_SetLoop
	cmp	[Byte cs:si+MS.loopcount],0
	jz	mt_jumpcnt
	dec	[Byte cs:si+MS.loopcount]
	jz	mt_Return
mt_jmploop:
	mov	al,[cs:si+MS.pattpos]
	mov	[cs:mt_PBreakPos],al
	mov	[Byte cs:mt_PBreakFlag],1
	ret

mt_jumpcnt:
	mov	[cs:si+MS.loopcount],al
	jmp	mt_jmploop

mt_SetLoop:
	mov	ax,[cs:mt_PatternPos]
	shr	ax,4
	and	ax,63
	mov	[cs:si+MS.pattpos],al
	ret
endp	mt_JumpLoop

; Effect 7 -- Set Tremolo Control
proc	mt_SetTremoloControl near
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	shl	al,4
	and	[Byte cs:si+MS.WaveControl],0Fh
	or	[cs:si+MS.WaveControl],al
	ret
endp	mt_SetTremoloControl

proc	mt_KarplusStrong near
;	 mov	 ax,[cs:si+MS.loopstart]
	mov	bx,ax
;	 mov	 ax,[cs:si+MS.RepLen]
	add	ax,ax
	sub	ax,2
karplop:
	; More here.
	ret
endp	mt_KarplusStrong

; Effect 9 -- Retrig Note
proc	mt_RetrigNote near
	mov	bl,[Byte cs:si+MS.cmdlo]
	and	bl,0Fh
	jz	mt_rtnend
	xor	ax,ax
	mov	al,[cs:mt_counter]
	or	al,al
	jnz	mt_rtnskp
	mov	ax,[cs:si+MS.Note]
	xchg	ah,al
	and	ax,0FFFh
	jnz	mt_rtnend
;	 mov	 [Byte cs:mt_counter],0
	xor	ax,ax
	mov	al,[cs:mt_counter]
mt_rtnskp:
	div	bl
	xchg	ah,al
	or	ax,ax
	jnz	mt_rtnend
mt_DoRetrig:
	xor	ax,ax
;	 mov	 [cs:bp+CD.sofs],ax
;	 mov	 ax,[cs:si+MS.Length]
	shl	ax,1
;	 mov	 [cs:bp+CD.slen],ax
;	 mov	 ax,[cs:si+MS.LoopStart]
;	 mov	 [cs:bp+CD.sloops],ax
;	 mov	 cx,[cs:si+MS.RepLen]
	add	ax,cx			; Add on the offset.
	add	ax,cx			; Add it again.
	sub	ax,3
;	 mov	 [cs:bp+CD.sloope],ax
mt_rtnend:
	ret
endp	mt_RetrigNote

; Effect A -- Volume Fine Up
proc	mt_VolumeFineUp near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
	jmp	mt_VolSlideUp
endp	mt_VolumeFineUp

; Effect B -- Volume Fine Down
proc	mt_VolumeFineDown near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	al,[cs:si+MS.cmdlo]
	and	al,0Fh
	jmp	mt_VolSlideDown2
endp	mt_VolumeFineDown

; Effect C -- Note Cut
proc	mt_NoteCut near
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	cmp	al,[cs:mt_counter]
	jnz	mt_Return
	mov	[Byte cs:si+MS.Volume],0
	mov	al,[Byte cs:si+MS.Volume]
        call    volequ
	ret
endp	mt_NoteCut

; Effect D -- Note Delay
proc	mt_NoteDelay near
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	cmp	al,[cs:mt_counter]
	jnz	mt_Return
	mov	ax,[cs:si]
	and	ax,0FFFh
	jz	mt_Return
	jmp	mt_DoRetrig
endp	mt_NoteDelay

; Effect E -- Pattern Delay
proc	mt_PatternDelay near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	cmp	[Byte cs:mt_PattDelayTime2],0
	jnz	mt_Return
	inc	al
	mov	[cs:mt_PattDelayTime],al
	ret
endp	mt_PatternDelay

; Effect F -- Funk It
proc	mt_FunkIt near
	cmp	[Byte cs:mt_counter],0
	jnz	mt_Return
	mov	al,[Byte cs:si+MS.cmdlo]
	and	al,0Fh
	shl	al,4
	and	[Byte cs:si+MS.GlissFunk],0Fh
	or	[cs:si+MS.GlissFunk],al
	or	al,al
	jz	mt_Return
	jmp	mt_UpdateFunk
endp	mt_FunkIt

proc	mt_UpdateFunk near
	xor	bx,bx
	mov	bl,[cs:si+MS.GlissFunk]
	shr	bl,4
	or	bl,bl
	jz	mt_funkend
	mov	al,[cs:bx+offset mt_FunkTable]
	add	[cs:si+MS.FunkOffset],al
	mov	al,[cs:si+MS.FunkOffset]
	and	al,10000000b
	jz	mt_funkend
	mov	[Byte cs:si+MS.FunkOffset],0
	; This is my best try at converting this code.
;	 mov	 ax,[cs:si+MS.LoopStart]
;	 mov	 bx,[cs:si+MS.RepLen]
;	 add	 ax,bx
;	 add	 ax,bx
;	 mov	 cx,[cs:si+MS.WaveStart]
;	 inc	 cx
;	 cmp	 cx,ax
;	 jb	 mt_funkok
;	 mov	 cx,[cs:si+MS.LoopStart]
;mt_funkok:
;	 mov	 [cs:si+MS.WaveStart],cx
;	 mov	 ax,-1
;	 mov	 bx,cx
;	 sub	 [cs:bx],ax
;	 mov	 ax,[cs:bx]
mt_funkend:
	ret
endp	mt_UpdateFunk



proc    volequ near
	push	bx
	mul	[Byte cs:si+MS.MasterVolume]
	mov	bl,ah
	xor	bh,bh
	shl	bx,1
	mov	dx,[cs:u_Command]
        mov     al,9
	out	dx,al
	inc	dx
;	 cmp	 [Byte cs:Voice],4
;	 jnz	  @@Leave
        mov     ax,[cs:bx+mt_VolTable]
	out	dx,ax
	pop	bx
	ret
@@Leave:
	mov	ax,0
	out	dx,ax
	pop	bx
	ret
endp	volequ

ends    Sound


segment zzzzzseg
	db	16 dup (?)
ends    zzzzzseg

        end     Start

