home *** CD-ROM | disk | FTP | other *** search
- ;
- ; 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.inc
- Include GUS.INC
- Include DMA.INC
- externdef Initalize_MOD_file :near
-
- NumOfActiveVoices EQU 32
-
-
- 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
- ;}}
- mov Channels,8
- cmp ds:Header.MOD_tag,'ATCO'
- je gotChans
- mov Channels,4
-
- Mov eax,ds:Header.MOD_tag
- mov al,' '
- cmp Eax,'NHC '
- jne get_ch
- mov al,byte ptr ds:Header.MOD_tag
- sub al,'0'
- mov Channels,al
- jmp gotChans
-
- get_ch: cmp word ptr ds:Header.MOD_tag[2],'HC'
- jne gotChans
- Mov ax,Word Ptr ds:Header.MOD_tag
- xchg al,ah
- Sub Ax,'00'
- aad
- mov Channels,Al
- 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 NumOfActiveVoices DUP (0)
- CurrentVoice_Args db NumOfActiveVoices DUP (0)
- Destinamtion_note dw NumOfActiveVoices DUP (0)
- Speed db 6
- Song_position db 0
- Current_Line db 0
- Current_Tick db 0
- Skip_volSet db False
- VoiceNote dw NumOfActiveVoices DUP (0)
- VoiceVolume db NumOfActiveVoices 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