home *** CD-ROM | disk | FTP | other *** search
/ Groovy Bytes: Behind the Moon / groovybytes.iso / GROOVY / SND_TOOL / FUNK108A.ZIP / DOS32V30.ZIP / EXAMPLES / MODPLAY.ZIP / MODPLAY.ASM < prev    next >
Encoding:
Assembly Source File  |  1995-06-07  |  32.8 KB  |  1,191 lines

  1. ;
  2. ;     A Cheap and nasty MOD player for the Ultrasound   V0.00000001ß
  3. ;
  4. ;   By Adam Seychell 
  5. ;
  6. ;    This MOD player was coded by me in about 3½ days of coding so please 
  7. ; don't tell me it's a piece of crap because I already know it is.  You may
  8. ; not even class it as a MOD player but rather a program which plays some 
  9. ; samples from a file in almost a random order.......
  10. ; When playing 8 channel MODs it uses a wopping 00.2% of CPU time
  11. ; on my 386DX 33MHz.  Oh well, you can't have everything. ( hehehehe )
  12. ; At least I have made an effort to comment the code. This means you
  13. ; should be able to follow it and do some improvements on it, like add
  14. ; support for more song effects :-)
  15. ;    This is MASM 6.1 only code. I know TASM is more popular but I love the
  16. ; .IF  .ELSE .ELSEIF and .ENDIF  directives of MASM. May be the latest TASM
  17. ; does these, I only have TASM v2.5 wich sucks compared to MASM 6.1.
  18. ;
  19. ;
  20. ;  
  21. .386
  22. .model flat
  23. .stack 4096
  24. .code
  25.  
  26. Include  Macros.inc
  27. Include  GUS.INC
  28. Include  DMA.INC
  29. externdef Initalize_MOD_file    :near
  30.  
  31. NumOfActiveVoices       EQU     32
  32.  
  33.  
  34. OPTION OLDSTRUCTS
  35.  
  36. GF1_Byte MACRO  _reg_,_data_
  37.         mov     DX,GF1_Reg_Select
  38.         mov     al,_reg_
  39.         out     dx,al
  40.         add     dl,2
  41.         mov     al,_data_
  42.         out     dx,al
  43. ENDM
  44.  
  45. GF1_Word MACRO  _reg_,_data_
  46.         mov     DX,GF1_Reg_Select
  47.         mov     al,_reg_
  48.         out     dx,al
  49.         inc     dl
  50.         mov     ax,_data_
  51.         out     dx,ax
  52. ENDM
  53.  
  54. ;=========================================================================
  55. ;
  56. ;  Three procedures to set the Starting , Ending and Current location
  57. ;  registers of the currently selected voice.
  58. ;
  59. ; Expects:  EAX with location
  60. ;
  61. ;
  62. ;
  63. ;=========================================================================
  64. EndingLocation PROC USES EBX
  65.         mov     bl,4
  66.         call Set_Voice_Address_Register
  67.         ret
  68. EndingLocation ENDP
  69.  
  70. StartingLocation PROC USES EBX
  71.         mov     bl,2
  72.         call Set_Voice_Address_Register
  73.         ret
  74. StartingLocation ENDP
  75.  
  76. CurrentLocation PROC USES EBX
  77.         mov     bl,0Ah
  78.         call Set_Voice_Address_Register
  79.         ret
  80. CurrentLocation  ENDP
  81.  
  82. Set_Voice_Address_Register PROC USES EAX EDI
  83.         mov     edi,eax
  84.         mov     dx, GF1_Reg_Select
  85.         mov     al,bl
  86.         out     dx,al
  87.         inc     dl
  88.         mov     eax, edi
  89.         shr     eax, 11
  90.         out     dx, ax                             ; location HIGH
  91.         dec     dl
  92.         mov     al,bl
  93.         inc     al
  94.         out     dx,al
  95.         inc     dl
  96.         mov     eax,edi
  97.         shl     eax,5
  98.         out     dx,ax                             ; location LOW
  99.         ret
  100. Set_Voice_Address_Register  ENDP
  101.  
  102.  
  103.  
  104.  
  105. ;===========================================================================
  106. ;
  107. ;  Set voice playing rate.
  108. ;
  109. ;  Expects:     CL  = Voice number
  110. ;               AX  = note to play. ( Amiga MOD file note values)
  111. ;
  112. ;===========================================================================
  113. Set_VoicePeriod PROC USES EDI EAX EDX
  114.                 mov     edi,eax
  115.                 mov     dx,GF1_Voice_Select
  116.             mov     al,cl
  117.             out     dx,al
  118.                 imul    edi,617400/NumOfActiveVoices
  119.                 xor     edx,edx
  120.                 mov     eax,3546894*1024
  121.                 div     edi
  122.                 mov     edi,eax
  123.                 mov     dx,GF1_Reg_Select
  124.                 mov     al,1
  125.                 out     dx,al               ; set the FC register
  126.                 inc     dl
  127.                 mov     ax,di
  128.                 out     dx,ax
  129.                 ret
  130. Set_VoicePeriod ENDP
  131.  
  132.  
  133.  
  134.  
  135. ;===========================================================================
  136. ;
  137. ; Set Voice Volume.
  138. ;
  139. ;  Expects:     CL  = Voice number
  140. ;               AL  = Linear volume.  Range 0 to 64
  141. ;
  142. ;===========================================================================
  143. Set_VoiceVolume  PROC USES EAX EDX EBX
  144.                 movzx   eax,al
  145.                 cmp     al,64
  146.                 jbe @f
  147.                  mov  al,64
  148.          @@:    mov     di,mt_VolTable[eax*2]
  149.                 shl     di,4
  150.                 mov     dx,GF1_Voice_Select
  151.             mov     al,cl
  152.             out     dx,al
  153.                 mov     dx,GF1_Reg_Select
  154.                 mov     al,9
  155.                 out     dx,al             ; set the current Volume register
  156.                 inc     dl
  157.                 mov     ax,di
  158.                 out     dx,ax
  159.                 ret
  160.  
  161. mt_VolTable     dw  0000
  162.                 dw  1750,2503,2701,2741,2781,2944,2964,2981
  163.                 dw  3000,3017,3034,3052,3070,3207,3215,3224
  164.                 dw  3332,3340,3248,3256,3263,3271,3279,3287
  165.                 dw  3294,3303,3310,3317,3325,3458,3462,3466
  166.                 dw  3469,3473,3478,3481,3484,3489,3492,3495
  167.                 dw  3499,3502,3506,3509,3513,3517,3520,3524
  168.                 dw  3528,3532,3534,3538,3543,3545,3549,3552
  169.                 dw  3556,3558,3563,3565,3570,3573,3577,3580
  170.  
  171. Set_VoiceVolume ENDP
  172.  
  173.  
  174. ;=========================================================================
  175. ;
  176. ;  Routine to start a voice playing a particular MOD instrument.
  177. ;
  178. ;  Expects:    BL with instument number  (0..30)
  179. ;              CL voice to play it on.   (0..Number of channels in song - 1 )
  180. ;
  181. ;  Ouptut:     Voice playing with looping off and IRQ's enabled
  182. ;              The ending position is set to the end of the sample
  183. ;              The starting position is set to the start of the sample
  184. ;              When the voice finishes playing the sample it will
  185. ;               generate an IRQ. The voice IRQ handler will then reload
  186. ;               the starting and ending postions so the voice will loop
  187. ;               in the sample. If the sample repeat length is less than
  188. ;               2 bytes then no looping will be used and the IRQ handler
  189. ;               will simply stop the voice.
  190. ;
  191. ;=========================================================================
  192. Play_instrument PROC USES EAX
  193.         mov     dx,GF1_Voice_Select
  194.         mov     al,cl
  195.         out     dx,al
  196.  
  197.         GF1_Byte  0, 00000010b          ; Stop Voice & IRQ's disabled
  198.                                         ; Must do this whenever altering
  199.                                         ; the starting ,ending  and current
  200.                                         ; location registers of the voice
  201.  
  202.         movzx   ebx,bl
  203.         cmp     bl,31
  204.         jae Invalid_instr
  205.         movzx   ecx,cl
  206.         mov     Voice_Instr[ECX],bl             ; save our instrument
  207.  
  208.         mov     eax,Instr_Start [EBX*4]
  209.         shl     eax,4
  210.         call    StartingLocation
  211.         call    CurrentLocation
  212.  
  213.         mov     eax,Instr_Length [EBX*4]
  214.         cmp     eax,10
  215.         jb Invalid_instr
  216.         add     eax,Instr_Start [EBX*4]
  217.         dec     eax
  218.         shl     eax,4
  219.         call    EndingLocation
  220.  
  221.         GF1_Byte  0, 00100000b          ; Looping disabled & IRQ's enabled
  222.  
  223. Invalid_instr:
  224.         ret
  225. Play_instrument ENDP
  226.  
  227.  
  228.  
  229.  
  230.  
  231.  
  232. ;==========================================================================
  233. ;
  234. ;   Voice Wave table IRQ handler. This procedure gets called when ever
  235. ; the Ultrasound generates a IRQ from a voice that has reached its ending
  236. ; address.
  237. ;
  238. ;==========================================================================
  239. Handle_VoiceWT PROC
  240. Local   Voices_Serviced :dword
  241.  
  242.         mov     Voices_Serviced,0
  243. SERVICE_LOOP:
  244.         mov     dx,GF1_Reg_Select
  245.         mov     al,8Fh
  246.         out     dx,al
  247.         add     dl,2
  248.         in      al,dx                   ; Read the IRQ source register
  249.         mov     ch,al
  250.         and     ch,11000000b
  251.         cmp     ch,11000000b
  252.         je FIFO_empty                   ; check if any IRQ are left
  253.         and     al,011111b
  254.         mov     cl,al                   ; Save voice number
  255.         mov     ebx,1
  256.         shl     ebx,cl
  257.         test    Voices_Serviced,ebx     ; see if voice has already been
  258.         jnz SERVICE_LOOP                ; serviced. If so then ignore it.
  259.         or    Voices_Serviced,ebx
  260.  
  261.         mov     dx,GF1_Voice_Select     ; Select Voice to operate with
  262.         mov     al,cl
  263.         out     dx,al
  264.  
  265.         GF1_Byte    0,00000010b        ; First disable IRQ's and stop it
  266.  
  267.        cmp  CL ,NumOfActiveVoices
  268.        jae Invalid_instr
  269.  
  270.         ;*    set up the voice to play the rest of the MOD instrument     *
  271.         ;------------------------------------------------------------------
  272.         ; Ok, the voice has just finished playing the MOD instrument and
  273.         ; has generated an IRQ because it has hit the ending address. Now
  274.         ; the voice must loop in the sample ( only of repeat length is
  275.         ; above 2). This is done simply by setting a new start and ending
  276.         ; position with lopping enabled. The voices IRQs can be disabled
  277.         ; since we are just let the voice loop continously.
  278.         movzx   ecx,cl
  279.         movzx   ebx,Voice_Instr[ECX]      ; get instrument voice is playing
  280.         cmp     bl,31
  281.         jae Invalid_instr
  282.  
  283.         mov     eax,Instr_ReptStart [EBX*4]
  284.         shl     eax,4
  285.         call    StartingLocation
  286.         call    CurrentLocation
  287.  
  288.         mov     eax,Instr_ReptLength [EBX*4]
  289.         cmp     eax,5
  290.         jb Invalid_instr
  291.         add     eax,Instr_ReptStart [EBX*4]
  292.         inc     eax
  293.         shl     eax,4
  294.         call    EndingLocation
  295.  
  296.         GF1_Byte  0, 00001000b          ;  IRQ's disabled, lopping enabled
  297.  
  298. Invalid_instr:
  299.  
  300.        jmp SERVICE_LOOP
  301.  
  302. FIFO_empty:
  303.         ret
  304. Handle_VoiceWT  ENDP
  305.  
  306.  
  307.  
  308.  
  309.  
  310.  
  311.  
  312.  
  313.  
  314. IRQ_source      db 0
  315. ;=========================================================================
  316. ;
  317. ;     The main Ultrasound Interrupt Handler
  318. ;
  319. ;   ( The GUS's IRQ handler is the hart of any Ultrasound code )
  320. ;
  321. ;=========================================================================
  322. GUS_ISR PROC
  323.         pushad
  324.         push    ds
  325.         mov     ax, Seg  GUS_ISR
  326.         mov     ds,ax
  327.  
  328. Service_GUS_IRQ_LOOP:
  329.         mov     dx,GF1_IRQ_status
  330.         in      al,dx
  331.         mov     IRQ_source,al
  332.  
  333. ;********  Check for Voice Wave Table IRQ **************
  334.         test    IRQ_source,00100000b
  335.         jz  Not_Voice_WaveTable
  336.  
  337.             call    Handle_VoiceWT
  338.  
  339. Not_Voice_WaveTable:
  340.  
  341. ;********  Check for Voice Volume Ramp IRQ **************
  342.         test    IRQ_source,01000000b
  343.         jz  Not_Voice_VolumeRamp
  344.  
  345.  
  346. Not_Voice_VolumeRamp:
  347.  
  348. ;******  Check for  TIMER 1 IRQ **************
  349.         test    IRQ_source,00100b
  350.         jz  Not_Timer1
  351.  
  352.          ;
  353.          ;  Run the MOD player.
  354.          ;
  355.                 call    MOD_Ticker
  356.          ;
  357.          ; Pulse IRQ enable bit from 0 to 1. This allows more Timer IRQ's
  358.          ;
  359.                outp  GF1_Reg_Select,45h
  360.                inp   GF1_Data_high
  361.                and   al,NOT 0100b               ; Clear Timer 1 IRQ
  362.                out   dx,al
  363.                or   al, 0100b                  ; Enable Timer 1 IRQ
  364.                out   dx,al
  365.  
  366. Not_Timer1:
  367.  
  368. ;********  Check for  TIMER 2 IRQ **************
  369.         test    IRQ_source,01000b
  370.         jz  Not_Timer2
  371.          ;
  372.          ; Pulse IRQ enable bit from 0 to 1 to allows more Timer IRQ's
  373.          ;
  374.                   outp  GF1_Reg_Select,45h
  375.               inp   GF1_Data_high
  376.                   and   al,NOT 1000b                  ; Clear Timer 2 IRQ
  377.               out   dx,al
  378.                   or   al, 1000b                      ; Enable Timer 2 IRQ
  379.               out   dx,al
  380.  
  381. Not_Timer2:
  382.  
  383. ;********  Check for  DMA Terminal Count  IRQ **************
  384.         test    IRQ_source,10000000b
  385.         jz  Not_DMA_TC
  386.  
  387.     ;
  388.     ;  Read DRAM DMA controll register to allow more DRAM DMA IRQs.
  389.     ;
  390.         mov     dx,GF1_REG_Select
  391.         mov     al,041h
  392.         out     dx,al
  393.         mov     dx,GF1_DATA_High
  394.         in      al,dx
  395.         or      DMAPlay_TC,1          ; Set flag to show we got a DMA IRQ
  396.  
  397.  
  398. Not_DMA_TC:
  399.  
  400.         test    IRQ_source,11101111b
  401.         jz Service_GUS_IRQ_LOOP        ; Keep on servicing IRQ's until there
  402.                                        ; is none left
  403.  
  404.         mov     al,20h                 ; Send EOI command to the PICs
  405.         out     20h,al
  406.         out     0a0h,al
  407.         pop     ds
  408.         popad
  409.         iretd
  410. GUS_ISR ENDP
  411.  
  412.  
  413. SampleRec     STRUC
  414. S_Name       db 22 DUP (0)
  415. S_Length     dw 0
  416. S_FineTune   db 0
  417. S_Volume     db 0
  418. S_ReptStart  dw 0
  419. S_ReptLength dw 0
  420. SampleRec ENDS
  421.  
  422. MODHeader STRUC
  423. SongName        db 20 DUP (0)
  424. Samples         SampleRec 31 DUP (<>)
  425. SongLength      db 0
  426. PRepeat         db 0
  427. Sequence        db 128 DUP (0)
  428. MOD_tag         dd 0
  429. MODHeader ENDS
  430.  
  431. Header MODHeader <>
  432.  
  433. Channels                db 0
  434.  
  435. FileHandleModF          dw 0
  436. filename_ptr            dd 0
  437. filenameEnd_ptr         dd 0
  438. align 4
  439. Pattern_PTR         dd 0
  440. DMA_DRAM_address        dd 0
  441. DMA_buffer_addr         dd 0
  442. DMA_buffer_phys         dd 0
  443. Patterns_Size           dd 0
  444. BytesRead               dd 0
  445. IRQ_chan     db 0
  446. DMA_chan     db 0
  447. DMAPlay_TC   db 0
  448. _loading_mesg   db 10,10,13,'Loading$'
  449. dummy_cmd_tail          db  1,' '
  450.  
  451. align 4
  452. Instr_ReptLength        dd 31 DUP (0)
  453. Instr_ReptStart         dd 31 DUP (0)
  454. Instr_Start             dd 31 DUP (0)
  455. Instr_Length            dd 31 DUP (0)
  456. Instr_Volume            db 31 DUP (0)
  457. Instr_FineTune          db 31 DUP (0)
  458. Voice_Instr             db 31 DUP (-1)
  459.  
  460.  
  461.  
  462.  
  463.  
  464. Start32:
  465.  
  466. Initalize_MOD_file:
  467.  
  468.  
  469. ;}}
  470. ;}}}    Read the ULTRASND to get the IRQ and DMA settings
  471. ;}}
  472.         call    GetUltraConfig
  473.         jc exit_error_gus
  474.         mov     DMA_chan,CL
  475.         mov     IRQ_chan,BL
  476.  
  477.  
  478.  
  479. ;}}
  480. ;}}}    Reset the Ultrasound and it's DMA and IRQ values.
  481. ;}}
  482.         call    Ultrasound_Reset
  483.         jc exit_error_gus
  484.  
  485.  
  486. ;}}
  487. ;}}}    Set the Ultrasound's IRQ vector
  488. ;}}
  489.         mov     edx,offset GUS_ISR
  490.         mov     cx,cs                           ; CX:EDX = selectro:offset
  491.         mov     bl,IRQ_chan                     ; Convert IRQ to interrupt
  492.         cmp     bl,8                            ; number
  493.         jb Jpic1
  494.         add     bl,60h
  495. Jpic1:  add     bl,8
  496.         mov     ax,0205h
  497.         int     31h
  498.  
  499.  
  500.  
  501. ;}}
  502. ;}}}  Load the MOD file header (song name, sample data, pattern order, ect )
  503. ;}}
  504.  
  505.         mov     ax,0EE02h                       ; GET DOS32 MEMORY INFO
  506.         int     31h                             ; Returns ESI -> PSP segment
  507.         mov     edi,esi
  508.         movzx   ecx,byte ptr [edi+80h]
  509.         inc     ecx
  510.         add     edi,81h
  511.         mov     al,' '
  512.         repe    scasb
  513.         dec     edi
  514.         and     ecx,ecx
  515.         jz exit_error_usage
  516.         push    edi
  517.         repne   scasb
  518.         mov     byte ptr [edi],0
  519.         mov     filenameEnd_ptr,edi
  520.         pop     edx
  521.         mov     filename_ptr,edx
  522.         mov     ax,3D02h            ; open a file function
  523.         int     21h
  524.         mov     FileHandleModF,ax
  525.         jc exit_error_file
  526.  
  527.         BlockRead        ModF,Offset Header,SIZEOF Header
  528.  
  529.  
  530. ;}}
  531. ;}}}     See how many channles the MOD
  532. ;}}
  533.         mov    Channels,8
  534.         cmp     ds:Header.MOD_tag,'ATCO'
  535.         je  gotChans
  536.         mov    Channels,4
  537.  
  538.         Mov     eax,ds:Header.MOD_tag
  539.         mov     al,' '
  540.         cmp     Eax,'NHC '
  541.         jne  get_ch
  542.         mov     al,byte ptr ds:Header.MOD_tag
  543.         sub     al,'0'
  544.         mov     Channels,al
  545.         jmp  gotChans
  546.  
  547. get_ch: cmp     word ptr ds:Header.MOD_tag[2],'HC'
  548.         jne  gotChans
  549.         Mov     ax,Word Ptr ds:Header.MOD_tag
  550.         xchg    al,ah
  551.         Sub     Ax,'00'
  552.         aad
  553.         mov     Channels,Al
  554. gotChans:
  555.  
  556.  
  557.  
  558. ;}}
  559. ;}}} Get the maximum pattern used, so can calculate where the samples start
  560. ;}}
  561.         xor     eax,eax
  562.         xor     ebx,ebx
  563. SrchM:  mov     CL,Header.Sequence[EBX]
  564.         cmp     CL,al
  565.         jbe @f
  566.         mov     al,CL
  567. @@:     inc     bl
  568.         cmp     bl,ds:Header.SongLength
  569.         jb SrchM
  570.         inc     eax
  571.                               ;--> EAX has maximum pattern + 1
  572.  
  573. ;}}
  574. ;}}}    Allocate some memory for all the patterns
  575. ;}}
  576.         movzx   ebx,Channels            ; calulate bytes needed for patterns
  577.         mul     ebx
  578.         shl     eax,8
  579.         mov     Patterns_Size,eax              ; save amount used
  580.         add     eax,0fffh
  581.         mov     edx,eax
  582.         mov     ax,0EE42h                       ; DOS32 allocate mem service
  583.         int     31h                            ; allocate EDX bytes
  584.         jc exit_error
  585.         mov     Pattern_PTR,edx                ; save pattern data pointer
  586.  
  587.  
  588. ;}}
  589. ;}}}    Load in the pattern data
  590. ;}}
  591.  
  592.         BlockRead       ModF,Pattern_PTR,Patterns_Size
  593.         jc exit_error
  594.  
  595. ;
  596. ;       Print the song name
  597. ;
  598.  
  599.         xor     ecx,ecx
  600. @@:     mov     al,Header.SongName[ecx]
  601.         cmp     al,0
  602.         jz @f
  603.         mov     ah,0Eh
  604.         xor     bh,bh
  605.         int     10h
  606.         inc     ecx
  607.         cmp     ecx,20
  608.         jb @b
  609. @@:
  610.         mov     edx,offset  _loading_mesg
  611.         mov     ah,9
  612.         int     21h
  613.  
  614.  
  615.  
  616. ;}}
  617. ;}}}    Extract all the instrument data form the file into a useful format
  618. ;}}
  619.  
  620.         xor     edi,edi
  621.         xor     esi,esi
  622.         xor     ecx,ecx
  623. SET_DATA_LOOP:
  624.         mov     Instr_Start [ecx*4],edi
  625.  
  626.         movzx   eax,Header.Samples.S_Length[ESI]
  627.         xchg    ah,al
  628.         shl     eax,1
  629.         add     edi,eax
  630.         mov     Instr_Length[ecx*4],eax
  631.  
  632.         movzx   eax,Header.Samples.S_reptLength[ESI]
  633.         xchg    ah,al
  634.         shl     eax,1
  635.         mov     Instr_ReptLength[ecx*4],eax
  636.  
  637.         movzx   eax,Header.Samples.S_reptStart[ESI]
  638.         xchg    ah,al
  639.         shl     eax,1
  640.         add     eax,Instr_Start [ecx*4]
  641.         mov     Instr_ReptStart [ecx*4],eax
  642.  
  643.         mov     al,Header.Samples.S_Volume[ESI]
  644.         mov     Instr_Volume[ecx],al
  645.  
  646.         mov     al,Header.Samples.S_FineTune[ESI]
  647.         mov     Instr_FineTune[ecx],al
  648.  
  649.         add     esi,SIZEOF SampleRec
  650.         inc     ecx
  651.         cmp     ecx,31
  652.         jb     SET_DATA_LOOP
  653.  
  654.  
  655. ;}}
  656. ;}}}    Setup Ultrasound for transfering DMA
  657. ;}}
  658.  
  659.       ; ***** Allocate a 16KB DMA buffer  ******
  660.  
  661.         mov     ax,0EE41h
  662.         int     31h
  663.         jc exit_error_mem
  664.         mov     DMA_buffer_addr,edx
  665.         mov     DMA_buffer_phys,ebx
  666.  
  667.  
  668. FILL_GUS_LOOP:
  669.  
  670.  
  671.      ;********** Read a 16KB of sample from the MOD file into the DMA buffer
  672.  
  673.         BlockRead  ModF,DMA_buffer_addr,4000h
  674.         jc  exit_error
  675.         mov     BytesRead,eax
  676.  
  677.         cli                             ; Must not be interrupted when
  678.                                         ; programming the DMA and GUS.
  679.  
  680.       ;****** program the 8237 DMA contoller  *************
  681.  
  682.         mov     al,01001000b             ; DMA mode register
  683.         mov     ah,DMA_Chan              ; Channel number ( 0..7 )
  684.         mov     ecx,04000h               ; Bytes to transfer
  685.         mov     ebx,DMA_buffer_PHYS      ; Physical base address
  686.         call    DMA_setup                ; Do it ( see DMA.ASM )
  687.  
  688.       ;********* Set the GUS's DMA DRAM staring address register *****
  689.  
  690.         mov     edi,DMA_DRAM_address
  691.         test    DMA_chan,100b
  692.         jz _8bitDMA
  693.            ; ---- do 16 bit DMA address translation ( see the SDK ) -----
  694.           mov     eax,edi
  695.           shr     edi,1
  696.           and     edi,01ffffh     ; zero out bit 17..19
  697.           and     eax,0c0000h     ; get bits 18 and 19
  698.           or      edi,eax
  699. _8bitDMA:
  700.         shr     edi,4
  701.         mov     al,042h                       ; DMA Start Address
  702.         outp    GF1_Reg_Select,42h
  703.         outpW    GF1_Data_Low,di
  704.  
  705.       ;******* Set the GUS's DRAM DMA Control Register  **********
  706.  
  707.         mov     ah,DMA_chan
  708.         and     ah,100b
  709.         mov     cl,00100001b       ;Write, 650KB/s, 8bit data, 16/8bit DMA
  710.         or      cl,ah
  711.         GF1_Byte  41h,cl           ; The DMA cycle will now start
  712.  
  713.         sti                        ; Leave critical state
  714.  
  715.    ;***** Wait around until the Ultrasound generates a DMA TC IRQ ******
  716.  
  717.                 mov     ecx,100000h
  718. waitTC:         test    DMAPlay_TC,1
  719.                 loopz  waitTC
  720.                 jz   exit_error_irq
  721.                 mov     DMAPlay_TC,0
  722.  
  723.         mov     al,'.'                        ; display loading progress
  724.         mov     ah,0Eh
  725.         xor     bh,bh
  726.         int     10h
  727.  
  728.         mov     eax,BytesRead
  729.         add     DMA_DRAM_address,eax
  730.         and     eax,eax
  731.         jnz FILL_GUS_LOOP
  732.  
  733.         ;Ok, finished filling the GUS's DRAM with samples
  734.  
  735. ;}}
  736. ;}}} ********  Cancel DRAM  DMA   ***************
  737. ;}}
  738.  
  739.         GF1_Byte 41h,00000000b                     ;stop DMA IRQ's
  740.  
  741.         close ModF                                 ; close the MOD file
  742.  
  743.  
  744. ;}}
  745. ;}}}  Set the number of active voices.
  746. ;}}
  747.         cli
  748.         GF1_Byte       0Eh,( NumOfActiveVoices -1 ) or 0C0h
  749.         sti
  750.  
  751.  
  752.  
  753. ;}}
  754. ;}}}  Set the panning positon for each voice
  755. ;}}
  756.         cli
  757.         mov     cl,0
  758.         mov     bl,2
  759. SetPlop:
  760.         outp    GF1_Voice_Select,cl
  761.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  762.         inc     cl
  763.         xor     bl,0fh
  764.         outp    GF1_Voice_Select,cl
  765.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  766.         inc     cl
  767.         outp    GF1_Voice_Select,cl
  768.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  769.         inc     cl
  770.         xor     bl,0fh
  771.         outp    GF1_Voice_Select,cl
  772.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  773.         inc     cl
  774.         cmp     cl,NumOfActiveVoices
  775.         jb SetPlop
  776.         sti
  777.  
  778. ;}}
  779. ;}}}  reset some of the song varibles
  780. ;}}
  781.         mov     Speed,6
  782.         mov     Song_position,0
  783.         mov     Current_Line,0
  784.         mov     Current_Tick,0
  785.  
  786.  
  787. ;}}
  788. ;}}}  Set Timer 1 ( 80µS )  to  50Hz ( period = 20000 µS )
  789. ;}}}   for the MOD player ticks
  790. ;}}
  791.  
  792.         outp            GF1_Timer_data,1      ; unmask timer 1 and start it
  793.         GF1_Byte        46h,255-( 20000/80 )  ; Timer 1 Count register
  794.         GF1_Byte        45h,0100b             ; Enable Timer 1 IRQ
  795.  
  796.  
  797.  
  798. ; THE SONG WILL START ON THE FIRST TIMER TICK ( See MOD_ticker proc )
  799.  
  800.  
  801.  
  802. ;************** LOAD AND EXECUTE A PROGRAM ( command.com ) *****************
  803.  
  804. ;
  805. ; search for the "COMSPEC=" environemnt varibles
  806. ;
  807.  
  808.         mov     ax,0EE02h            ; Get DOS32 address information
  809.         int     31h                  ; Returns EDI -> environment address
  810. get_str_loop:
  811.         cmp     dword ptr [edi],'SMOC'          ; cmp the string "COMSPEC="
  812.         jne not_string
  813.         cmp     dword ptr [edi+4],'=CEP'
  814.         je got_string                           ; if equal exit loop
  815. not_string:
  816.         mov     al,0                            ; Scan environment for a zero
  817.         mov     ecx,20000                       ; and get next varible
  818.         repne   scasb
  819.         jmp  get_str_loop                       ; loop around again
  820.  
  821. got_string:
  822.         add     edi,8
  823.         mov     edx,edi                         ; EDX hold address of the
  824.                                                 ; COMSPEC string
  825.  
  826.  
  827. ;
  828. ; Call the 32bit version of the "load and execute" DOS service
  829. ;
  830.         mov     ah,4Bh
  831.         mov     al,0
  832.         mov     edi,00000                       ; DS:EDI -> envrironment
  833.         mov     esi,Offset dummy_cmd_tail       ; DS:ESI -> command tail
  834.                                                 ; DS:EDX -> ASCIIZ string
  835.         Int     21h
  836.  
  837.  
  838.  
  839.  
  840.         GF1_Byte        45h,0000b       ; Stop the GUS's timer 1
  841.  
  842.  
  843.         mov     edx,offset keyplay_mesg
  844.         mov     ah,9
  845.         int     21h
  846.  
  847. byebye:
  848.  
  849.         mov     cl,-1
  850.  
  851. @@:
  852.         mov     ah,0
  853.         int     16h
  854.         cmp    ah,1
  855.         je exit4
  856.         mov     al,ah
  857.         sub     al,3Bh
  858.         cmp     al,10
  859.         jb Set_a_note
  860.  
  861.          mov     bl,ah
  862.          sub     bl,2
  863.          cmp     bl,31
  864.          jae done
  865.          inc     cl
  866.          cli
  867.          call    play_instrument
  868.          mov     al,64
  869.          call    Set_VoiceVolume
  870.          mov     ax,Curnt_Note
  871.          call    Set_VoicePeriod
  872.          sti
  873.          jmp done
  874.  
  875. Set_a_note:
  876.         xor     ah,ah
  877.         add     ax,3
  878.         shl     ax,7
  879.         mov     Curnt_Note,ax
  880.         call    Set_VoicePeriod
  881.  
  882. done:
  883.         cmp     cl,NumOfActiveVoices
  884.         jb   @b
  885.         jmp byebye
  886.  
  887. exit4:
  888.         mov     edx,offset end_mesg
  889.         mov     ah,9
  890.         int     21h
  891. exit2:
  892.         call    Ultrasound_init
  893.  
  894.         mov     ah,4ch
  895.         int     21h
  896. Curnt_Note      dw 200
  897.  
  898. keyplay_mesg    db 'Hit the keyboard keys to play the MOD instruments.  ESC to abort',10,13,36
  899. end_mesg    db 10,10,13,'Your computer is back to normal.',10,13,36
  900.  
  901. parmBlock       db 10h dup (0)
  902. meme db 0,0,0,0
  903. exit_error_gus:
  904.         mov     edx,offset mesg1
  905.         jmp   exit_error
  906. mesg1   db ' Ultrasound card not found $'
  907. exit_error_irq:
  908.         mov     edx,offset mesg2
  909.         jmp   exit_error
  910. mesg2   db ' GF1 not generating IRQ''s. Try a different IRQ setting',10,13
  911.         db ' in the environemnt string ULTRASND$'
  912. exit_error_mem:
  913.         mov     edx,offset mesg3
  914.         jmp   exit_error
  915. mesg3   db 'Not enough base memory for DMA buffer ( need about 75KB free )$'
  916. exit_error_file:
  917.         mov     edx,offset mesg4
  918.         jmp   exit_error
  919. mesg4   db 'can''t open mod file $'
  920. exit_error_usage:
  921.         mov     edx,offset mesg5
  922.         jmp   exit_error
  923. mesg5   db 'Usage:    modplay  <filename.MOD> ',10,13,36
  924.  
  925.  
  926. exit_error:
  927.         mov     ah,9
  928.         int     21h
  929.         jmp exit2
  930.  
  931.  
  932.  
  933.  
  934.  
  935.  
  936.  
  937.  
  938.  
  939.      ;  song varibles
  940. ;----------------------------------------------------------------
  941. align 4
  942. CurrentVoice_effect     dd NumOfActiveVoices DUP (0)
  943. CurrentVoice_Args       db NumOfActiveVoices DUP (0)
  944. Destinamtion_note       dw NumOfActiveVoices DUP (0)
  945. Speed               db 6
  946. Song_position       db 0
  947. Current_Line        db 0
  948. Current_Tick            db 0
  949. Skip_volSet             db False
  950. VoiceNote               dw NumOfActiveVoices DUP (0)
  951. VoiceVolume             db NumOfActiveVoices DUP (0)
  952. ;----------------------------------------------------------------
  953.  
  954.  
  955.  
  956.  
  957. ;=========================================================================
  958. ;
  959. ;  OK.  This is basically the entire MOD players code. This procedure is
  960. ; driven by the Ultrasounds TIMER 1 interrupt which is set to a rate of
  961. ; 50Hz.
  962. ;
  963. ;
  964. ;=========================================================================
  965. MOD_Ticker PROC
  966.  
  967.  
  968.    CMP  Current_Tick , 0
  969.    jne PlaySame_line
  970.  
  971.         call    Play_Pattern_line
  972.  
  973. PlaySame_line:
  974.  
  975.  
  976.         ;*** update the effects for each voice *******
  977.         xor     ecx,ecx
  978. update_effect:
  979.         mov     AH,CurrentVoice_Args[ecx]          ; get the argument
  980.         call    CurrentVoice_effect[ecx*4]
  981.         inc     ecx
  982.         cmp     cl,channels
  983.         jb update_effect
  984.  
  985.  
  986.          inc    Current_Tick
  987.          mov    al,Current_Tick
  988.          cmp    al,Speed
  989.          jb   dtick
  990.           mov    Current_Tick,0
  991.           inc    Current_Line
  992.           cmp    Current_Line,64
  993.           jb  dLine
  994.            mov    Current_Line,0
  995.            inc    Song_position
  996.            mov   al,Song_position
  997.            cmp   al,Header.SongLength
  998.            jb dpatt
  999.            mov Song_position,0
  1000.  
  1001. dpatt:
  1002. dtick:
  1003. dLine:
  1004.  
  1005.         ret
  1006. MOD_Ticker ENDP
  1007.  
  1008.  
  1009. ;--------------------------------------------------------------------------
  1010. ;  Play all the instruments on the current pattern line.
  1011. ; Also update any new effects on the voices.
  1012. ;--------------------------------------------------------------------------
  1013. Play_Pattern_line PROC
  1014.         movzx   eax,Song_position
  1015.         movzx   esi,Header.Sequence[eax]        ; Get the current pattern
  1016.         movzx   eax,channels                    ;Get bytes per line
  1017.         shl     eax,2
  1018.         mov     ebx,eax
  1019.         shl     ebx,6                           ; get bytes per pattern
  1020.         imul    esi,ebx                         ; multiply by pattern number
  1021.         mul     Current_Line                    ; multiply by line number
  1022.         add     esi,eax
  1023.  
  1024.         add     esi,Pattern_PTR
  1025.         xor     ecx,ecx
  1026.         mov     Skip_volSet,False
  1027.  
  1028.            ;******* get the Pattern data for each channel ***
  1029. playlineloop:
  1030.  
  1031.                 mov   CurrentVoice_effect[ecx*4], offset Nul_effect
  1032.              ;*** get the effect ( AL) and argument ( AH )*******
  1033.                 mov     al,[esi+2]
  1034.                 and     al,0Fh
  1035.                 mov     ah,[esi+3]
  1036.                 and     ax,ax
  1037.                 jz no_effect_used
  1038.  
  1039.         .IF  al ==  1
  1040.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1041.                   mov   Destinamtion_note[ecx*2],0
  1042.         .ELSEIF al == 2
  1043.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1044.                   mov   Destinamtion_note[ecx*2],-1
  1045.  
  1046.        .ELSEIF al == 3
  1047.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1048.                   mov   bx,[esi]
  1049.                   xchg  bh,bl
  1050.                   and   ebx,0fffh
  1051.                   jz @f
  1052.                    mov   Destinamtion_note[ecx*2],bx
  1053.               @@: and  ah,ah
  1054.                   jnz  @f
  1055.                    mov  ah,CurrentVoice_Args[ecx]   ; use last argument
  1056.                 @@:
  1057. ;        .ELSEIF al == 4         ;****** vibrato **************
  1058. ;                  mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1059. ;                  mov   Destinamtion_note[ecx*2],-1
  1060.  
  1061.         .ELSEIF al == 0Ah         ;******  Volume slide **************
  1062.                   mov   CurrentVoice_effect[ecx*4], offset Slide_Volume
  1063.         .ELSEIF al == 0Ch         ;****** Set Volume **************
  1064.                   mov    al,ah
  1065.                   cmp    al,64
  1066.                   jna @f
  1067.                    mov  al,64
  1068.               @@: mov     VoiceVolume[ecx],al     ; save the volume
  1069.                   call   Set_VoiceVolume
  1070.                   mov   Skip_volSet,True
  1071.         .ELSEIF al == 0Dh         ;****** Pattern Break **************
  1072.                 movzx   ebx,ah
  1073.                 and     bl,0f0h
  1074.                 shr     bl,4
  1075.                 imul    ebx,10
  1076.                 mov     bh,ah
  1077.                 and     bh,0Fh
  1078.                 add     bl,bh
  1079.                 dec     bl
  1080.                 mov     Current_Line,bl
  1081.                 inc     Song_position
  1082.                 mov     Current_tick,-1
  1083.                 ret
  1084.  
  1085.         .ELSEIF al == 0Fh       ; ******* Set the Speed **********
  1086.                  cmp ah,0
  1087.              jz ignSp
  1088.              cmp ah,31
  1089.                  ja ignSp
  1090.                  mov  speed,ah
  1091.            ignSp:
  1092.  
  1093.         .ENDIF
  1094.  
  1095.               mov   CurrentVoice_Args[ecx],ah       ; save the argument
  1096.  
  1097. no_effect_used:
  1098.                 mov     bl,[esi+2]       ;Extract instrument number into BL
  1099.             shr     bl,4
  1100.             mov     bh,[esi]
  1101.             and     bh,0f0h
  1102.                 or      bl,bh
  1103.                 sub     bl,1
  1104.                 jc   Skip_Note
  1105.  
  1106.                 mov     eax,[esi]              ; get Note value
  1107.                 xchg    ah,al
  1108.                 and     eax,00fffh
  1109.                 jz    Skip_Note
  1110.                 mov    VoiceNote[ecx*2],ax      ; save the note
  1111.                 call    Set_VoicePeriod
  1112.  
  1113.                 cmp Skip_volSet , True
  1114.                 je @f
  1115.                 mov     al,Instr_Volume[EBX]
  1116.                 mov     VoiceVolume[ecx],al     ; save the volume
  1117.                 call    Set_VoiceVolume
  1118.             @@:
  1119.                 call    Play_instrument          ; Start the voice playing
  1120. Skip_Note:
  1121.                add     esi,4
  1122.                inc     cl
  1123.                cmp     cl,Channels
  1124.                jb  playlineloop
  1125.  
  1126.                 ret
  1127. Play_Pattern_line ENDP
  1128.  
  1129.  
  1130.  
  1131.  
  1132.  
  1133.  
  1134.  
  1135. ;***** nul effect *************
  1136. Nul_Effect PROC
  1137.         ret
  1138. Nul_Effect ENDP
  1139.  
  1140. ;******** Slide the Volume *************
  1141. Slide_Volume PROC
  1142.         test    ah,0F0h
  1143.         jnz  UP
  1144.         and     ah,0Fh
  1145.         sub   VoiceVolume[ecx],ah
  1146.         jnc  @f
  1147.          mov   VoiceVolume[ecx],0
  1148. @@:     jmp exit1
  1149.  
  1150.  
  1151. UP:     shr     ah,4
  1152.         add   VoiceVolume[ecx],ah
  1153.         cmp   VoiceVolume[ecx],64
  1154.         jbe @f
  1155.          mov   VoiceVolume[ecx],64
  1156.   @@:
  1157.  
  1158. exit1:  mov     al,VoiceVolume[ecx]
  1159.         call    Set_VoiceVolume
  1160.         ret
  1161. Slide_Volume ENDP
  1162.  
  1163. ;****** Slide to Note ( dec/increase period )************
  1164. Slide_To_note PROC
  1165.         mov   bx,Destinamtion_note[ecx*2]  ; get Note value to slide towords
  1166.         movzx ax,ah
  1167.         cmp VoiceNote[ecx*2],bx
  1168.         jb up
  1169.         ja down
  1170.         ret
  1171. up:
  1172.         add VoiceNote[ecx*2],ax
  1173.         jmp exit1
  1174. down:
  1175.         sub VoiceNote[ecx*2],ax
  1176. exit1:
  1177.         mov     ax,VoiceNote[ecx*2]
  1178.         .If   ax > 856
  1179.           mov  ax,856
  1180.         .elseif ax < 113
  1181.           mov  ax,113
  1182.         .endif
  1183.         mov     VoiceNote[ecx*2],ax
  1184.         call    Set_VoicePeriod
  1185. Skip_Note:
  1186.         ret
  1187. Slide_To_note ENDP
  1188.  
  1189.  
  1190. END Start32
  1191.