home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 5
/
ctrom5b.zip
/
ctrom5b
/
PROGRAM
/
DIVERSEN
/
DOS32V3B
/
EXAMPLES
/
MODPLAY.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-03-08
|
33KB
|
1,182 lines
;
; A Cheap and nasty MOD player for the Ultrasound V0.00000001ß
;
; By Adam Seychell
;
; This MOD player was coded by me in about 3½ days of coding so please
; don't tell me it's a piece of crap because I already know it is. You may
; not even class it as a MOD player but rather a program which plays some
; samples from a file in almost a random order.......
; When playing 8 channel MODs it uses a wopping 00.2% of CPU time
; on my 386DX 33MHz. Oh well, you can't have everything. ( hehehehe )
; At least I have made an effort to comment the code. This means you
; should be able to follow it and do some improvements on it, like add
; support for more song effects :-)
; This is MASM 6.1 only code. I know TASM is more popular but I love the
; .IF .ELSE .ELSEIF and .ENDIF directives of MASM. May be the latest TASM
; does these, I only have TASM v2.5 wich sucks compared to MASM 6.1.
;
;
;
.386
.model flat
.stack 4096
.code
Include Macros.386
Include GUS.INC
Include DMA.INC
externdef Initalize_MOD_file :near
NumOfActiveVoices EQU 16
OPTION OLDSTRUCTS
GF1_Byte MACRO _reg_,_data_
mov DX,GF1_Reg_Select
mov al,_reg_
out dx,al
add dl,2
mov al,_data_
out dx,al
ENDM
GF1_Word MACRO _reg_,_data_
mov DX,GF1_Reg_Select
mov al,_reg_
out dx,al
inc dl
mov ax,_data_
out dx,ax
ENDM
;=========================================================================
;
; Three procedures to set the Starting , Ending and Current location
; registers of the currently selected voice.
;
; Expects: EAX with location
;
;
;
;=========================================================================
EndingLocation PROC USES EBX
mov bl,4
call Set_Voice_Address_Register
ret
EndingLocation ENDP
StartingLocation PROC USES EBX
mov bl,2
call Set_Voice_Address_Register
ret
StartingLocation ENDP
CurrentLocation PROC USES EBX
mov bl,0Ah
call Set_Voice_Address_Register
ret
CurrentLocation ENDP
Set_Voice_Address_Register PROC USES EAX EDI
mov edi,eax
mov dx, GF1_Reg_Select
mov al,bl
out dx,al
inc dl
mov eax, edi
shr eax, 11
out dx, ax ; location HIGH
dec dl
mov al,bl
inc al
out dx,al
inc dl
mov eax,edi
shl eax,5
out dx,ax ; location LOW
ret
Set_Voice_Address_Register ENDP
;===========================================================================
;
; Set voice playing rate.
;
; Expects: CL = Voice number
; AX = note to play. ( Amiga MOD file note values)
;
;===========================================================================
Set_VoicePeriod PROC USES EDI EAX EDX
mov edi,eax
mov dx,GF1_Voice_Select
mov al,cl
out dx,al
imul edi,617400/NumOfActiveVoices
xor edx,edx
mov eax,3546894*1024
div edi
mov edi,eax
mov dx,GF1_Reg_Select
mov al,1
out dx,al ; set the FC register
inc dl
mov ax,di
out dx,ax
ret
Set_VoicePeriod ENDP
;===========================================================================
;
; Set Voice Volume.
;
; Expects: CL = Voice number
; AL = Linear volume. Range 0 to 64
;
;===========================================================================
Set_VoiceVolume PROC USES EAX EDX EBX
movzx eax,al
cmp al,64
jbe @f
mov al,64
@@: mov di,mt_VolTable[eax*2]
shl di,4
mov dx,GF1_Voice_Select
mov al,cl
out dx,al
mov dx,GF1_Reg_Select
mov al,9
out dx,al ; set the current Volume register
inc dl
mov ax,di
out dx,ax
ret
mt_VolTable dw 0000
dw 1750,2503,2701,2741,2781,2944,2964,2981
dw 3000,3017,3034,3052,3070,3207,3215,3224
dw 3332,3340,3248,3256,3263,3271,3279,3287
dw 3294,3303,3310,3317,3325,3458,3462,3466
dw 3469,3473,3478,3481,3484,3489,3492,3495
dw 3499,3502,3506,3509,3513,3517,3520,3524
dw 3528,3532,3534,3538,3543,3545,3549,3552
dw 3556,3558,3563,3565,3570,3573,3577,3580
Set_VoiceVolume ENDP
;=========================================================================
;
; Routine to start a voice playing a particular MOD instrument.
;
; Expects: BL with instument number (0..30)
; CL voice to play it on. (0..Number of channels in song - 1 )
;
; Ouptut: Voice playing with looping off and IRQ's enabled
; The ending position is set to the end of the sample
; The starting position is set to the start of the sample
; When the voice finishes playing the sample it will
; generate an IRQ. The voice IRQ handler will then reload
; the starting and ending postions so the voice will loop
; in the sample. If the sample repeat length is less than
; 2 bytes then no looping will be used and the IRQ handler
; will simply stop the voice.
;
;=========================================================================
Play_instrument PROC USES EAX
mov dx,GF1_Voice_Select
mov al,cl
out dx,al
GF1_Byte 0, 00000010b ; Stop Voice & IRQ's disabled
; Must do this whenever altering
; the starting ,ending and current
; location registers of the voice
movzx ebx,bl
cmp bl,31
jae Invalid_instr
movzx ecx,cl
mov Voice_Instr[ECX],bl ; save our instrument
mov eax,Instr_Start [EBX*4]
shl eax,4
call StartingLocation
call CurrentLocation
mov eax,Instr_Length [EBX*4]
cmp eax,10
jb Invalid_instr
add eax,Instr_Start [EBX*4]
dec eax
shl eax,4
call EndingLocation
GF1_Byte 0, 00100000b ; Looping disabled & IRQ's enabled
Invalid_instr:
ret
Play_instrument ENDP
;==========================================================================
;
; Voice Wave table IRQ handler. This procedure gets called when ever
; the Ultrasound generates a IRQ from a voice that has reached its ending
; address.
;
;==========================================================================
Handle_VoiceWT PROC
Local Voices_Serviced :dword
mov Voices_Serviced,0
SERVICE_LOOP:
mov dx,GF1_Reg_Select
mov al,8Fh
out dx,al
add dl,2
in al,dx ; Read the IRQ source register
mov ch,al
and ch,11000000b
cmp ch,11000000b
je FIFO_empty ; check if any IRQ are left
and al,011111b
mov cl,al ; Save voice number
mov ebx,1
shl ebx,cl
test Voices_Serviced,ebx ; see if voice has already been
jnz SERVICE_LOOP ; serviced. If so then ignore it.
or Voices_Serviced,ebx
mov dx,GF1_Voice_Select ; Select Voice to operate with
mov al,cl
out dx,al
GF1_Byte 0,00000010b ; First disable IRQ's and stop it
cmp CL ,NumOfActiveVoices
jae Invalid_instr
;* set up the voice to play the rest of the MOD instrument *
;------------------------------------------------------------------
; Ok, the voice has just finished playing the MOD instrument and
; has generated an IRQ because it has hit the ending address. Now
; the voice must loop in the sample ( only of repeat length is
; above 2). This is done simply by setting a new start and ending
; position with lopping enabled. The voices IRQs can be disabled
; since we are just let the voice loop continously.
movzx ecx,cl
movzx ebx,Voice_Instr[ECX] ; get instrument voice is playing
cmp bl,31
jae Invalid_instr
mov eax,Instr_ReptStart [EBX*4]
shl eax,4
call StartingLocation
call CurrentLocation
mov eax,Instr_ReptLength [EBX*4]
cmp eax,5
jb Invalid_instr
add eax,Instr_ReptStart [EBX*4]
inc eax
shl eax,4
call EndingLocation
GF1_Byte 0, 00001000b ; IRQ's disabled, lopping enabled
Invalid_instr:
jmp SERVICE_LOOP
FIFO_empty:
ret
Handle_VoiceWT ENDP
IRQ_source db 0
;=========================================================================
;
; The main Ultrasound Interrupt Handler
;
; ( The GUS's IRQ handler is the hart of any Ultrasound code )
;
;=========================================================================
GUS_ISR PROC
pushad
push ds
mov ax, Seg GUS_ISR
mov ds,ax
Service_GUS_IRQ_LOOP:
mov dx,GF1_IRQ_status
in al,dx
mov IRQ_source,al
;******** Check for Voice Wave Table IRQ **************
test IRQ_source,00100000b
jz Not_Voice_WaveTable
call Handle_VoiceWT
Not_Voice_WaveTable:
;******** Check for Voice Volume Ramp IRQ **************
test IRQ_source,01000000b
jz Not_Voice_VolumeRamp
Not_Voice_VolumeRamp:
;****** Check for TIMER 1 IRQ **************
test IRQ_source,00100b
jz Not_Timer1
;
; Run the MOD player.
;
call MOD_Ticker
;
; Pulse IRQ enable bit from 0 to 1. This allows more Timer IRQ's
;
outp GF1_Reg_Select,45h
inp GF1_Data_high
and al,NOT 0100b ; Clear Timer 1 IRQ
out dx,al
or al, 0100b ; Enable Timer 1 IRQ
out dx,al
Not_Timer1:
;******** Check for TIMER 2 IRQ **************
test IRQ_source,01000b
jz Not_Timer2
;
; Pulse IRQ enable bit from 0 to 1 to allows more Timer IRQ's
;
outp GF1_Reg_Select,45h
inp GF1_Data_high
and al,NOT 1000b ; Clear Timer 2 IRQ
out dx,al
or al, 1000b ; Enable Timer 2 IRQ
out dx,al
Not_Timer2:
;******** Check for DMA Terminal Count IRQ **************
test IRQ_source,10000000b
jz Not_DMA_TC
;
; Read DRAM DMA controll register to allow more DRAM DMA IRQs.
;
mov dx,GF1_REG_Select
mov al,041h
out dx,al
mov dx,GF1_DATA_High
in al,dx
or DMAPlay_TC,1 ; Set flag to show we got a DMA IRQ
Not_DMA_TC:
test IRQ_source,11101111b
jz Service_GUS_IRQ_LOOP ; Keep on servicing IRQ's until there
; is none left
mov al,20h ; Send EOI command to the PICs
out 20h,al
out 0a0h,al
pop ds
popad
iretd
GUS_ISR ENDP
SampleRec STRUC
S_Name db 22 DUP (0)
S_Length dw 0
S_FineTune db 0
S_Volume db 0
S_ReptStart dw 0
S_ReptLength dw 0
SampleRec ENDS
MODHeader STRUC
SongName db 20 DUP (0)
Samples SampleRec 31 DUP (<>)
SongLength db 0
PRepeat db 0
Sequence db 128 DUP (0)
MOD_tag dd 0
MODHeader ENDS
Header MODHeader <>
Channels db 0
FileHandleModF dw 0
filename_ptr dd 0
filenameEnd_ptr dd 0
align 4
Pattern_PTR dd 0
DMA_DRAM_address dd 0
DMA_buffer_addr dd 0
DMA_buffer_phys dd 0
Patterns_Size dd 0
BytesRead dd 0
IRQ_chan db 0
DMA_chan db 0
DMAPlay_TC db 0
_loading_mesg db 10,10,13,'Loading$'
dummy_cmd_tail db 1,' '
align 4
Instr_ReptLength dd 31 DUP (0)
Instr_ReptStart dd 31 DUP (0)
Instr_Start dd 31 DUP (0)
Instr_Length dd 31 DUP (0)
Instr_Volume db 31 DUP (0)
Instr_FineTune db 31 DUP (0)
Voice_Instr db 31 DUP (-1)
Start32:
Initalize_MOD_file:
;}}
;}}} Read the ULTRASND to get the IRQ and DMA settings
;}}
call GetUltraConfig
jc exit_error_gus
mov DMA_chan,CL
mov IRQ_chan,BL
;}}
;}}} Reset the Ultrasound and it's DMA and IRQ values.
;}}
call Ultrasound_Reset
jc exit_error_gus
;}}
;}}} Set the Ultrasound's IRQ vector
;}}
mov edx,offset GUS_ISR
mov cx,cs ; CX:EDX = selectro:offset
mov bl,IRQ_chan ; Convert IRQ to interrupt
cmp bl,8 ; number
jb Jpic1
add bl,60h
Jpic1: add bl,8
mov ax,0205h
int 31h
;}}
;}}} Load the MOD file header (song name, sample data, pattern order, ect )
;}}
mov ax,0EE02h ; GET DOS32 MEMORY INFO
int 31h ; Returns ESI -> PSP segment
mov edi,esi
movzx ecx,byte ptr [edi+80h]
inc ecx
add edi,81h
mov al,' '
repe scasb
dec edi
and ecx,ecx
jz exit_error_usage
push edi
repne scasb
mov byte ptr [edi],0
mov filenameEnd_ptr,edi
pop edx
mov filename_ptr,edx
mov ax,3D02h ; open a file function
int 21h
mov FileHandleModF,ax
jc exit_error_file
BlockRead ModF,Offset Header,SIZEOF Header
;}}
;}}} See how many channles the MOD uses 4,8,16 or 32
;}}
mov Channels,32
cmp ds:Header.MOD_tag,'HC32'
je gotChans
mov Channels,16
cmp ds:Header.MOD_tag,'HC61'
je gotChans
mov Channels,8
cmp ds:Header.MOD_tag,'NHC8'
je gotChans
cmp ds:Header.MOD_tag,'ATCO'
je gotChans
mov Channels,4
gotChans:
;}}
;}}} Get the maximum pattern used, so can calculate where the samples start
;}}
xor eax,eax
xor ebx,ebx
SrchM: mov CL,Header.Sequence[EBX]
cmp CL,al
jbe @f
mov al,CL
@@: inc bl
cmp bl,ds:Header.SongLength
jb SrchM
inc eax
;--> EAX has maximum pattern + 1
;}}
;}}} Allocate some memory for all the patterns
;}}
movzx ebx,Channels ; calulate bytes needed for patterns
mul ebx
shl eax,8
mov Patterns_Size,eax ; save amount used
add eax,0fffh
mov edx,eax
mov ax,0EE42h ; DOS32 allocate mem service
int 31h ; allocate EDX bytes
jc exit_error
mov Pattern_PTR,edx ; save pattern data pointer
;}}
;}}} Load in the pattern data
;}}
BlockRead ModF,Pattern_PTR,Patterns_Size
jc exit_error
;
; Print the song name
;
xor ecx,ecx
@@: mov al,Header.SongName[ecx]
cmp al,0
jz @f
mov ah,0Eh
xor bh,bh
int 10h
inc ecx
cmp ecx,20
jb @b
@@:
mov edx,offset _loading_mesg
mov ah,9
int 21h
;}}
;}}} Extract all the instrument data form the file into a useful format
;}}
xor edi,edi
xor esi,esi
xor ecx,ecx
SET_DATA_LOOP:
mov Instr_Start [ecx*4],edi
movzx eax,Header.Samples.S_Length[ESI]
xchg ah,al
shl eax,1
add edi,eax
mov Instr_Length[ecx*4],eax
movzx eax,Header.Samples.S_reptLength[ESI]
xchg ah,al
shl eax,1
mov Instr_ReptLength[ecx*4],eax
movzx eax,Header.Samples.S_reptStart[ESI]
xchg ah,al
shl eax,1
add eax,Instr_Start [ecx*4]
mov Instr_ReptStart [ecx*4],eax
mov al,Header.Samples.S_Volume[ESI]
mov Instr_Volume[ecx],al
mov al,Header.Samples.S_FineTune[ESI]
mov Instr_FineTune[ecx],al
add esi,SIZEOF SampleRec
inc ecx
cmp ecx,31
jb SET_DATA_LOOP
;}}
;}}} Setup Ultrasound for transfering DMA
;}}
; ***** Allocate a 16KB DMA buffer ******
mov ax,0EE41h
int 31h
jc exit_error_mem
mov DMA_buffer_addr,edx
mov DMA_buffer_phys,ebx
FILL_GUS_LOOP:
;********** Read a 16KB of sample from the MOD file into the DMA buffer
BlockRead ModF,DMA_buffer_addr,4000h
jc exit_error
mov BytesRead,eax
cli ; Must not be interrupted when
; programming the DMA and GUS.
;****** program the 8237 DMA contoller *************
mov al,01001000b ; DMA mode register
mov ah,DMA_Chan ; Channel number ( 0..7 )
mov ecx,04000h ; Bytes to transfer
mov ebx,DMA_buffer_PHYS ; Physical base address
call DMA_setup ; Do it ( see DMA.ASM )
;********* Set the GUS's DMA DRAM staring address register *****
mov edi,DMA_DRAM_address
test DMA_chan,100b
jz _8bitDMA
; ---- do 16 bit DMA address translation ( see the SDK ) -----
mov eax,edi
shr edi,1
and edi,01ffffh ; zero out bit 17..19
and eax,0c0000h ; get bits 18 and 19
or edi,eax
_8bitDMA:
shr edi,4
mov al,042h ; DMA Start Address
outp GF1_Reg_Select,42h
outpW GF1_Data_Low,di
;******* Set the GUS's DRAM DMA Control Register **********
mov ah,DMA_chan
and ah,100b
mov cl,00100001b ;Write, 650KB/s, 8bit data, 16/8bit DMA
or cl,ah
GF1_Byte 41h,cl ; The DMA cycle will now start
sti ; Leave critical state
;***** Wait around until the Ultrasound generates a DMA TC IRQ ******
mov ecx,100000h
waitTC: test DMAPlay_TC,1
loopz waitTC
jz exit_error_irq
mov DMAPlay_TC,0
mov al,'.' ; display loading progress
mov ah,0Eh
xor bh,bh
int 10h
mov eax,BytesRead
add DMA_DRAM_address,eax
and eax,eax
jnz FILL_GUS_LOOP
;Ok, finished filling the GUS's DRAM with samples
;}}
;}}} ******** Cancel DRAM DMA ***************
;}}
GF1_Byte 41h,00000000b ;stop DMA IRQ's
close ModF ; close the MOD file
;}}
;}}} Set the number of active voices.
;}}
cli
GF1_Byte 0Eh,( NumOfActiveVoices -1 ) or 0C0h
sti
;}}
;}}} Set the panning positon for each voice
;}}
cli
mov cl,0
mov bl,2
SetPlop:
outp GF1_Voice_Select,cl
GF1_Byte 0Ch,bl ; Voice Pan register
inc cl
xor bl,0fh
outp GF1_Voice_Select,cl
GF1_Byte 0Ch,bl ; Voice Pan register
inc cl
outp GF1_Voice_Select,cl
GF1_Byte 0Ch,bl ; Voice Pan register
inc cl
xor bl,0fh
outp GF1_Voice_Select,cl
GF1_Byte 0Ch,bl ; Voice Pan register
inc cl
cmp cl,NumOfActiveVoices
jb SetPlop
sti
;}}
;}}} reset some of the song varibles
;}}
mov Speed,6
mov Song_position,0
mov Current_Line,0
mov Current_Tick,0
;}}
;}}} Set Timer 1 ( 80µS ) to 50Hz ( period = 20000 µS )
;}}} for the MOD player ticks
;}}
outp GF1_Timer_data,1 ; unmask timer 1 and start it
GF1_Byte 46h,255-( 20000/80 ) ; Timer 1 Count register
GF1_Byte 45h,0100b ; Enable Timer 1 IRQ
; THE SONG WILL START ON THE FIRST TIMER TICK ( See MOD_ticker proc )
;************** LOAD AND EXECUTE A PROGRAM ( command.com ) *****************
;
; search for the "COMSPEC=" environemnt varibles
;
mov ax,0EE02h ; Get DOS32 address information
int 31h ; Returns EDI -> environment address
get_str_loop:
cmp dword ptr [edi],'SMOC' ; cmp the string "COMSPEC="
jne not_string
cmp dword ptr [edi+4],'=CEP'
je got_string ; if equal exit loop
not_string:
mov al,0 ; Scan environment for a zero
mov ecx,20000 ; and get next varible
repne scasb
jmp get_str_loop ; loop around again
got_string:
add edi,8
mov edx,edi ; EDX hold address of the
; COMSPEC string
;
; Call the 32bit version of the "load and execute" DOS service
;
mov ah,4Bh
mov al,0
mov edi,00000 ; DS:EDI -> envrironment
mov esi,Offset dummy_cmd_tail ; DS:ESI -> command tail
; DS:EDX -> ASCIIZ string
Int 21h
GF1_Byte 45h,0000b ; Stop the GUS's timer 1
mov edx,offset keyplay_mesg
mov ah,9
int 21h
byebye:
mov cl,-1
@@:
mov ah,0
int 16h
cmp ah,1
je exit4
mov al,ah
sub al,3Bh
cmp al,10
jb Set_a_note
mov bl,ah
sub bl,2
cmp bl,31
jae done
inc cl
cli
call play_instrument
mov al,64
call Set_VoiceVolume
mov ax,Curnt_Note
call Set_VoicePeriod
sti
jmp done
Set_a_note:
xor ah,ah
add ax,3
shl ax,7
mov Curnt_Note,ax
call Set_VoicePeriod
done:
cmp cl,NumOfActiveVoices
jb @b
jmp byebye
exit4:
mov edx,offset end_mesg
mov ah,9
int 21h
exit2:
call Ultrasound_init
mov ah,4ch
int 21h
Curnt_Note dw 200
keyplay_mesg db 'Hit the keyboard keys to play the MOD instruments. ESC to abort',10,13,36
end_mesg db 10,10,13,'Your computer is back to normal.',10,13,36
parmBlock db 10h dup (0)
meme db 0,0,0,0
exit_error_gus:
mov edx,offset mesg1
jmp exit_error
mesg1 db ' Ultrasound card not found $'
exit_error_irq:
mov edx,offset mesg2
jmp exit_error
mesg2 db ' GF1 not generating IRQ''s. Try a different IRQ setting',10,13
db ' in the environemnt string ULTRASND$'
exit_error_mem:
mov edx,offset mesg3
jmp exit_error
mesg3 db 'Not enough base memory for DMA buffer ( need about 75KB free )$'
exit_error_file:
mov edx,offset mesg4
jmp exit_error
mesg4 db 'can''t open mod file $'
exit_error_usage:
mov edx,offset mesg5
jmp exit_error
mesg5 db 'Usage: modplay <filename.MOD> ',10,13,36
exit_error:
mov ah,9
int 21h
jmp exit2
; song varibles
;----------------------------------------------------------------
align 4
CurrentVoice_effect dd 16 DUP (0)
CurrentVoice_Args db 16 DUP (0)
Destinamtion_note dw 16 DUP (0)
Speed db 6
Song_position db 0
Current_Line db 0
Current_Tick db 0
Skip_volSet db False
VoiceNote dw 16 DUP (0)
VoiceVolume db 16 DUP (0)
;----------------------------------------------------------------
;=========================================================================
;
; OK. This is basically the entire MOD players code. This procedure is
; driven by the Ultrasounds TIMER 1 interrupt which is set to a rate of
; 50Hz.
;
;
;=========================================================================
MOD_Ticker PROC
CMP Current_Tick , 0
jne PlaySame_line
call Play_Pattern_line
PlaySame_line:
;*** update the effects for each voice *******
xor ecx,ecx
update_effect:
mov AH,CurrentVoice_Args[ecx] ; get the argument
call CurrentVoice_effect[ecx*4]
inc ecx
cmp cl,channels
jb update_effect
inc Current_Tick
mov al,Current_Tick
cmp al,Speed
jb dtick
mov Current_Tick,0
inc Current_Line
cmp Current_Line,64
jb dLine
mov Current_Line,0
inc Song_position
mov al,Song_position
cmp al,Header.SongLength
jb dpatt
mov Song_position,0
dpatt:
dtick:
dLine:
ret
MOD_Ticker ENDP
;--------------------------------------------------------------------------
; Play all the instruments on the current pattern line.
; Also update any new effects on the voices.
;--------------------------------------------------------------------------
Play_Pattern_line PROC
movzx eax,Song_position
movzx esi,Header.Sequence[eax] ; Get the current pattern
movzx eax,channels ;Get bytes per line
shl eax,2
mov ebx,eax
shl ebx,6 ; get bytes per pattern
imul esi,ebx ; multiply by pattern number
mul Current_Line ; multiply by line number
add esi,eax
add esi,Pattern_PTR
xor ecx,ecx
mov Skip_volSet,False
;******* get the Pattern data for each channel ***
playlineloop:
mov CurrentVoice_effect[ecx*4], offset Nul_effect
;*** get the effect ( AL) and argument ( AH )*******
mov al,[esi+2]
and al,0Fh
mov ah,[esi+3]
and ax,ax
jz no_effect_used
.IF al == 1
mov CurrentVoice_effect[ecx*4], offset Slide_to_Note
mov Destinamtion_note[ecx*2],0
.ELSEIF al == 2
mov CurrentVoice_effect[ecx*4], offset Slide_to_Note
mov Destinamtion_note[ecx*2],-1
.ELSEIF al == 3
mov CurrentVoice_effect[ecx*4], offset Slide_to_Note
mov bx,[esi]
xchg bh,bl
and ebx,0fffh
jz @f
mov Destinamtion_note[ecx*2],bx
@@: and ah,ah
jnz @f
mov ah,CurrentVoice_Args[ecx] ; use last argument
@@:
; .ELSEIF al == 4 ;****** vibrato **************
; mov CurrentVoice_effect[ecx*4], offset Slide_to_Note
; mov Destinamtion_note[ecx*2],-1
.ELSEIF al == 0Ah ;****** Volume slide **************
mov CurrentVoice_effect[ecx*4], offset Slide_Volume
.ELSEIF al == 0Ch ;****** Set Volume **************
mov al,ah
cmp al,64
jna @f
mov al,64
@@: mov VoiceVolume[ecx],al ; save the volume
call Set_VoiceVolume
mov Skip_volSet,True
.ELSEIF al == 0Dh ;****** Pattern Break **************
movzx ebx,ah
and bl,0f0h
shr bl,4
imul ebx,10
mov bh,ah
and bh,0Fh
add bl,bh
dec bl
mov Current_Line,bl
inc Song_position
mov Current_tick,-1
ret
.ELSEIF al == 0Fh ; ******* Set the Speed **********
cmp ah,0
jz ignSp
cmp ah,31
ja ignSp
mov speed,ah
ignSp:
.ENDIF
mov CurrentVoice_Args[ecx],ah ; save the argument
no_effect_used:
mov bl,[esi+2] ;Extract instrument number into BL
shr bl,4
mov bh,[esi]
and bh,0f0h
or bl,bh
sub bl,1
jc Skip_Note
mov eax,[esi] ; get Note value
xchg ah,al
and eax,00fffh
jz Skip_Note
mov VoiceNote[ecx*2],ax ; save the note
call Set_VoicePeriod
cmp Skip_volSet , True
je @f
mov al,Instr_Volume[EBX]
mov VoiceVolume[ecx],al ; save the volume
call Set_VoiceVolume
@@:
call Play_instrument ; Start the voice playing
Skip_Note:
add esi,4
inc cl
cmp cl,Channels
jb playlineloop
ret
Play_Pattern_line ENDP
;***** nul effect *************
Nul_Effect PROC
ret
Nul_Effect ENDP
;******** Slide the Volume *************
Slide_Volume PROC
test ah,0F0h
jnz UP
and ah,0Fh
sub VoiceVolume[ecx],ah
jnc @f
mov VoiceVolume[ecx],0
@@: jmp exit1
UP: shr ah,4
add VoiceVolume[ecx],ah
cmp VoiceVolume[ecx],64
jbe @f
mov VoiceVolume[ecx],64
@@:
exit1: mov al,VoiceVolume[ecx]
call Set_VoiceVolume
ret
Slide_Volume ENDP
;****** Slide to Note ( dec/increase period )************
Slide_To_note PROC
mov bx,Destinamtion_note[ecx*2] ; get Note value to slide towords
movzx ax,ah
cmp VoiceNote[ecx*2],bx
jb up
ja down
ret
up:
add VoiceNote[ecx*2],ax
jmp exit1
down:
sub VoiceNote[ecx*2],ax
exit1:
mov ax,VoiceNote[ecx*2]
.If ax > 856
mov ax,856
.elseif ax < 113
mov ax,113
.endif
mov VoiceNote[ecx*2],ax
call Set_VoicePeriod
Skip_Note:
ret
Slide_To_note ENDP
END Start32