home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 5 / ctrom5b.zip / ctrom5b / PROGRAM / DIVERSEN / DOS32V3B / EXAMPLES / MODPLAY.ASM < prev    next >
Assembly Source File  |  1995-03-08  |  33KB  |  1,182 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.386
  27. Include  GUS.INC
  28. Include  DMA.INC
  29. externdef Initalize_MOD_file    :near
  30.  
  31. NumOfActiveVoices       EQU     16
  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 uses 4,8,16 or 32
  532. ;}}
  533.         mov    Channels,32
  534.         cmp     ds:Header.MOD_tag,'HC32'
  535.         je  gotChans
  536.         mov    Channels,16
  537.         cmp     ds:Header.MOD_tag,'HC61'
  538.         je  gotChans
  539.         mov    Channels,8
  540.         cmp     ds:Header.MOD_tag,'NHC8'
  541.         je  gotChans
  542.         cmp     ds:Header.MOD_tag,'ATCO'
  543.         je  gotChans
  544.         mov    Channels,4
  545. gotChans:
  546.  
  547.  
  548.  
  549. ;}}
  550. ;}}} Get the maximum pattern used, so can calculate where the samples start
  551. ;}}
  552.         xor     eax,eax
  553.         xor     ebx,ebx
  554. SrchM:  mov     CL,Header.Sequence[EBX]
  555.         cmp     CL,al
  556.         jbe @f
  557.         mov     al,CL
  558. @@:     inc     bl
  559.         cmp     bl,ds:Header.SongLength
  560.         jb SrchM
  561.         inc     eax
  562.                               ;--> EAX has maximum pattern + 1
  563.  
  564. ;}}
  565. ;}}}    Allocate some memory for all the patterns
  566. ;}}
  567.         movzx   ebx,Channels            ; calulate bytes needed for patterns
  568.         mul     ebx
  569.         shl     eax,8
  570.         mov     Patterns_Size,eax              ; save amount used
  571.         add     eax,0fffh
  572.         mov     edx,eax
  573.         mov     ax,0EE42h                       ; DOS32 allocate mem service
  574.         int     31h                            ; allocate EDX bytes
  575.         jc exit_error
  576.         mov     Pattern_PTR,edx                ; save pattern data pointer
  577.  
  578.  
  579. ;}}
  580. ;}}}    Load in the pattern data
  581. ;}}
  582.  
  583.         BlockRead       ModF,Pattern_PTR,Patterns_Size
  584.         jc exit_error
  585.  
  586. ;
  587. ;       Print the song name
  588. ;
  589.  
  590.         xor     ecx,ecx
  591. @@:     mov     al,Header.SongName[ecx]
  592.         cmp     al,0
  593.         jz @f
  594.         mov     ah,0Eh
  595.         xor     bh,bh
  596.         int     10h
  597.         inc     ecx
  598.         cmp     ecx,20
  599.         jb @b
  600. @@:
  601.         mov     edx,offset  _loading_mesg
  602.         mov     ah,9
  603.         int     21h
  604.  
  605.  
  606.  
  607. ;}}
  608. ;}}}    Extract all the instrument data form the file into a useful format
  609. ;}}
  610.  
  611.         xor     edi,edi
  612.         xor     esi,esi
  613.         xor     ecx,ecx
  614. SET_DATA_LOOP:
  615.         mov     Instr_Start [ecx*4],edi
  616.  
  617.         movzx   eax,Header.Samples.S_Length[ESI]
  618.         xchg    ah,al
  619.         shl     eax,1
  620.         add     edi,eax
  621.         mov     Instr_Length[ecx*4],eax
  622.  
  623.         movzx   eax,Header.Samples.S_reptLength[ESI]
  624.         xchg    ah,al
  625.         shl     eax,1
  626.         mov     Instr_ReptLength[ecx*4],eax
  627.  
  628.         movzx   eax,Header.Samples.S_reptStart[ESI]
  629.         xchg    ah,al
  630.         shl     eax,1
  631.         add     eax,Instr_Start [ecx*4]
  632.         mov     Instr_ReptStart [ecx*4],eax
  633.  
  634.         mov     al,Header.Samples.S_Volume[ESI]
  635.         mov     Instr_Volume[ecx],al
  636.  
  637.         mov     al,Header.Samples.S_FineTune[ESI]
  638.         mov     Instr_FineTune[ecx],al
  639.  
  640.         add     esi,SIZEOF SampleRec
  641.         inc     ecx
  642.         cmp     ecx,31
  643.         jb     SET_DATA_LOOP
  644.  
  645.  
  646. ;}}
  647. ;}}}    Setup Ultrasound for transfering DMA
  648. ;}}
  649.  
  650.       ; ***** Allocate a 16KB DMA buffer  ******
  651.  
  652.         mov     ax,0EE41h
  653.         int     31h
  654.         jc exit_error_mem
  655.         mov     DMA_buffer_addr,edx
  656.         mov     DMA_buffer_phys,ebx
  657.  
  658.  
  659. FILL_GUS_LOOP:
  660.  
  661.  
  662.      ;********** Read a 16KB of sample from the MOD file into the DMA buffer
  663.  
  664.         BlockRead  ModF,DMA_buffer_addr,4000h
  665.         jc  exit_error
  666.         mov     BytesRead,eax
  667.  
  668.         cli                             ; Must not be interrupted when
  669.                                         ; programming the DMA and GUS.
  670.  
  671.       ;****** program the 8237 DMA contoller  *************
  672.  
  673.         mov     al,01001000b             ; DMA mode register
  674.         mov     ah,DMA_Chan              ; Channel number ( 0..7 )
  675.         mov     ecx,04000h               ; Bytes to transfer
  676.         mov     ebx,DMA_buffer_PHYS      ; Physical base address
  677.         call    DMA_setup                ; Do it ( see DMA.ASM )
  678.  
  679.       ;********* Set the GUS's DMA DRAM staring address register *****
  680.  
  681.         mov     edi,DMA_DRAM_address
  682.         test    DMA_chan,100b
  683.         jz _8bitDMA
  684.            ; ---- do 16 bit DMA address translation ( see the SDK ) -----
  685.           mov     eax,edi
  686.           shr     edi,1
  687.           and     edi,01ffffh     ; zero out bit 17..19
  688.           and     eax,0c0000h     ; get bits 18 and 19
  689.           or      edi,eax
  690. _8bitDMA:
  691.         shr     edi,4
  692.         mov     al,042h                       ; DMA Start Address
  693.         outp    GF1_Reg_Select,42h
  694.         outpW    GF1_Data_Low,di
  695.  
  696.       ;******* Set the GUS's DRAM DMA Control Register  **********
  697.  
  698.         mov     ah,DMA_chan
  699.         and     ah,100b
  700.         mov     cl,00100001b       ;Write, 650KB/s, 8bit data, 16/8bit DMA
  701.         or      cl,ah
  702.         GF1_Byte  41h,cl           ; The DMA cycle will now start
  703.  
  704.         sti                        ; Leave critical state
  705.  
  706.    ;***** Wait around until the Ultrasound generates a DMA TC IRQ ******
  707.  
  708.                 mov     ecx,100000h
  709. waitTC:         test    DMAPlay_TC,1
  710.                 loopz  waitTC
  711.                 jz   exit_error_irq
  712.                 mov     DMAPlay_TC,0
  713.  
  714.         mov     al,'.'                        ; display loading progress
  715.         mov     ah,0Eh
  716.         xor     bh,bh
  717.         int     10h
  718.  
  719.         mov     eax,BytesRead
  720.         add     DMA_DRAM_address,eax
  721.         and     eax,eax
  722.         jnz FILL_GUS_LOOP
  723.  
  724.         ;Ok, finished filling the GUS's DRAM with samples
  725.  
  726. ;}}
  727. ;}}} ********  Cancel DRAM  DMA   ***************
  728. ;}}
  729.  
  730.         GF1_Byte 41h,00000000b                     ;stop DMA IRQ's
  731.  
  732.         close ModF                                 ; close the MOD file
  733.  
  734.  
  735. ;}}
  736. ;}}}  Set the number of active voices.
  737. ;}}
  738.         cli
  739.         GF1_Byte       0Eh,( NumOfActiveVoices -1 ) or 0C0h
  740.         sti
  741.  
  742.  
  743.  
  744. ;}}
  745. ;}}}  Set the panning positon for each voice
  746. ;}}
  747.         cli
  748.         mov     cl,0
  749.         mov     bl,2
  750. SetPlop:
  751.         outp    GF1_Voice_Select,cl
  752.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  753.         inc     cl
  754.         xor     bl,0fh
  755.         outp    GF1_Voice_Select,cl
  756.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  757.         inc     cl
  758.         outp    GF1_Voice_Select,cl
  759.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  760.         inc     cl
  761.         xor     bl,0fh
  762.         outp    GF1_Voice_Select,cl
  763.         GF1_Byte      0Ch,bl                      ; Voice Pan register
  764.         inc     cl
  765.         cmp     cl,NumOfActiveVoices
  766.         jb SetPlop
  767.         sti
  768.  
  769. ;}}
  770. ;}}}  reset some of the song varibles
  771. ;}}
  772.         mov     Speed,6
  773.         mov     Song_position,0
  774.         mov     Current_Line,0
  775.         mov     Current_Tick,0
  776.  
  777.  
  778. ;}}
  779. ;}}}  Set Timer 1 ( 80µS )  to  50Hz ( period = 20000 µS )
  780. ;}}}   for the MOD player ticks
  781. ;}}
  782.  
  783.         outp            GF1_Timer_data,1      ; unmask timer 1 and start it
  784.         GF1_Byte        46h,255-( 20000/80 )  ; Timer 1 Count register
  785.         GF1_Byte        45h,0100b             ; Enable Timer 1 IRQ
  786.  
  787.  
  788.  
  789. ; THE SONG WILL START ON THE FIRST TIMER TICK ( See MOD_ticker proc )
  790.  
  791.  
  792.  
  793. ;************** LOAD AND EXECUTE A PROGRAM ( command.com ) *****************
  794.  
  795. ;
  796. ; search for the "COMSPEC=" environemnt varibles
  797. ;
  798.  
  799.         mov     ax,0EE02h            ; Get DOS32 address information
  800.         int     31h                  ; Returns EDI -> environment address
  801. get_str_loop:
  802.         cmp     dword ptr [edi],'SMOC'          ; cmp the string "COMSPEC="
  803.         jne not_string
  804.         cmp     dword ptr [edi+4],'=CEP'
  805.         je got_string                           ; if equal exit loop
  806. not_string:
  807.         mov     al,0                            ; Scan environment for a zero
  808.         mov     ecx,20000                       ; and get next varible
  809.         repne   scasb
  810.         jmp  get_str_loop                       ; loop around again
  811.  
  812. got_string:
  813.         add     edi,8
  814.         mov     edx,edi                         ; EDX hold address of the
  815.                                                 ; COMSPEC string
  816.  
  817.  
  818. ;
  819. ; Call the 32bit version of the "load and execute" DOS service
  820. ;
  821.         mov     ah,4Bh
  822.         mov     al,0
  823.         mov     edi,00000                       ; DS:EDI -> envrironment
  824.         mov     esi,Offset dummy_cmd_tail       ; DS:ESI -> command tail
  825.                                                 ; DS:EDX -> ASCIIZ string
  826.         Int     21h
  827.  
  828.  
  829.  
  830.  
  831.         GF1_Byte        45h,0000b       ; Stop the GUS's timer 1
  832.  
  833.  
  834.         mov     edx,offset keyplay_mesg
  835.         mov     ah,9
  836.         int     21h
  837.  
  838. byebye:
  839.  
  840.         mov     cl,-1
  841.  
  842. @@:
  843.         mov     ah,0
  844.         int     16h
  845.         cmp    ah,1
  846.         je exit4
  847.         mov     al,ah
  848.         sub     al,3Bh
  849.         cmp     al,10
  850.         jb Set_a_note
  851.  
  852.          mov     bl,ah
  853.          sub     bl,2
  854.          cmp     bl,31
  855.          jae done
  856.          inc     cl
  857.          cli
  858.          call    play_instrument
  859.          mov     al,64
  860.          call    Set_VoiceVolume
  861.          mov     ax,Curnt_Note
  862.          call    Set_VoicePeriod
  863.          sti
  864.          jmp done
  865.  
  866. Set_a_note:
  867.         xor     ah,ah
  868.         add     ax,3
  869.         shl     ax,7
  870.         mov     Curnt_Note,ax
  871.         call    Set_VoicePeriod
  872.  
  873. done:
  874.         cmp     cl,NumOfActiveVoices
  875.         jb   @b
  876.         jmp byebye
  877.  
  878. exit4:
  879.         mov     edx,offset end_mesg
  880.         mov     ah,9
  881.         int     21h
  882. exit2:
  883.         call    Ultrasound_init
  884.  
  885.         mov     ah,4ch
  886.         int     21h
  887. Curnt_Note      dw 200
  888.  
  889. keyplay_mesg    db 'Hit the keyboard keys to play the MOD instruments.  ESC to abort',10,13,36
  890. end_mesg    db 10,10,13,'Your computer is back to normal.',10,13,36
  891.  
  892. parmBlock       db 10h dup (0)
  893. meme db 0,0,0,0
  894. exit_error_gus:
  895.         mov     edx,offset mesg1
  896.         jmp   exit_error
  897. mesg1   db ' Ultrasound card not found $'
  898. exit_error_irq:
  899.         mov     edx,offset mesg2
  900.         jmp   exit_error
  901. mesg2   db ' GF1 not generating IRQ''s. Try a different IRQ setting',10,13
  902.         db ' in the environemnt string ULTRASND$'
  903. exit_error_mem:
  904.         mov     edx,offset mesg3
  905.         jmp   exit_error
  906. mesg3   db 'Not enough base memory for DMA buffer ( need about 75KB free )$'
  907. exit_error_file:
  908.         mov     edx,offset mesg4
  909.         jmp   exit_error
  910. mesg4   db 'can''t open mod file $'
  911. exit_error_usage:
  912.         mov     edx,offset mesg5
  913.         jmp   exit_error
  914. mesg5   db 'Usage:    modplay  <filename.MOD> ',10,13,36
  915.  
  916.  
  917. exit_error:
  918.         mov     ah,9
  919.         int     21h
  920.         jmp exit2
  921.  
  922.  
  923.  
  924.  
  925.  
  926.  
  927.  
  928.  
  929.  
  930.      ;  song varibles
  931. ;----------------------------------------------------------------
  932. align 4
  933. CurrentVoice_effect     dd 16 DUP (0)
  934. CurrentVoice_Args       db 16 DUP (0)
  935. Destinamtion_note       dw 16 DUP (0)
  936. Speed               db 6
  937. Song_position       db 0
  938. Current_Line        db 0
  939. Current_Tick            db 0
  940. Skip_volSet             db False
  941. VoiceNote               dw 16 DUP (0)
  942. VoiceVolume         db 16 DUP (0)
  943. ;----------------------------------------------------------------
  944.  
  945.  
  946.  
  947.  
  948. ;=========================================================================
  949. ;
  950. ;  OK.  This is basically the entire MOD players code. This procedure is
  951. ; driven by the Ultrasounds TIMER 1 interrupt which is set to a rate of
  952. ; 50Hz.
  953. ;
  954. ;
  955. ;=========================================================================
  956. MOD_Ticker PROC
  957.  
  958.  
  959.    CMP  Current_Tick , 0
  960.    jne PlaySame_line
  961.  
  962.         call    Play_Pattern_line
  963.  
  964. PlaySame_line:
  965.  
  966.  
  967.         ;*** update the effects for each voice *******
  968.         xor     ecx,ecx
  969. update_effect:
  970.         mov     AH,CurrentVoice_Args[ecx]          ; get the argument
  971.         call    CurrentVoice_effect[ecx*4]
  972.         inc     ecx
  973.         cmp     cl,channels
  974.         jb update_effect
  975.  
  976.  
  977.          inc    Current_Tick
  978.          mov    al,Current_Tick
  979.          cmp    al,Speed
  980.          jb   dtick
  981.           mov    Current_Tick,0
  982.           inc    Current_Line
  983.           cmp    Current_Line,64
  984.           jb  dLine
  985.            mov    Current_Line,0
  986.            inc    Song_position
  987.            mov   al,Song_position
  988.            cmp   al,Header.SongLength
  989.            jb dpatt
  990.            mov Song_position,0
  991.  
  992. dpatt:
  993. dtick:
  994. dLine:
  995.  
  996.         ret
  997. MOD_Ticker ENDP
  998.  
  999.  
  1000. ;--------------------------------------------------------------------------
  1001. ;  Play all the instruments on the current pattern line.
  1002. ; Also update any new effects on the voices.
  1003. ;--------------------------------------------------------------------------
  1004. Play_Pattern_line PROC
  1005.         movzx   eax,Song_position
  1006.         movzx   esi,Header.Sequence[eax]        ; Get the current pattern
  1007.         movzx   eax,channels                    ;Get bytes per line
  1008.         shl     eax,2
  1009.         mov     ebx,eax
  1010.         shl     ebx,6                           ; get bytes per pattern
  1011.         imul    esi,ebx                         ; multiply by pattern number
  1012.         mul     Current_Line                    ; multiply by line number
  1013.         add     esi,eax
  1014.  
  1015.         add     esi,Pattern_PTR
  1016.         xor     ecx,ecx
  1017.         mov     Skip_volSet,False
  1018.  
  1019.            ;******* get the Pattern data for each channel ***
  1020. playlineloop:
  1021.  
  1022.                 mov   CurrentVoice_effect[ecx*4], offset Nul_effect
  1023.              ;*** get the effect ( AL) and argument ( AH )*******
  1024.                 mov     al,[esi+2]
  1025.                 and     al,0Fh
  1026.                 mov     ah,[esi+3]
  1027.                 and     ax,ax
  1028.                 jz no_effect_used
  1029.  
  1030.         .IF  al ==  1
  1031.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1032.                   mov   Destinamtion_note[ecx*2],0
  1033.         .ELSEIF al == 2
  1034.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1035.                   mov   Destinamtion_note[ecx*2],-1
  1036.  
  1037.        .ELSEIF al == 3
  1038.                   mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1039.                   mov   bx,[esi]
  1040.                   xchg  bh,bl
  1041.                   and   ebx,0fffh
  1042.                   jz @f
  1043.                    mov   Destinamtion_note[ecx*2],bx
  1044.               @@: and  ah,ah
  1045.                   jnz  @f
  1046.                    mov  ah,CurrentVoice_Args[ecx]   ; use last argument
  1047.                 @@:
  1048. ;        .ELSEIF al == 4         ;****** vibrato **************
  1049. ;                  mov   CurrentVoice_effect[ecx*4], offset Slide_to_Note
  1050. ;                  mov   Destinamtion_note[ecx*2],-1
  1051.  
  1052.         .ELSEIF al == 0Ah         ;******  Volume slide **************
  1053.                   mov   CurrentVoice_effect[ecx*4], offset Slide_Volume
  1054.         .ELSEIF al == 0Ch         ;****** Set Volume **************
  1055.                   mov    al,ah
  1056.                   cmp    al,64
  1057.                   jna @f
  1058.                    mov  al,64
  1059.               @@: mov     VoiceVolume[ecx],al     ; save the volume
  1060.                   call   Set_VoiceVolume
  1061.                   mov   Skip_volSet,True
  1062.         .ELSEIF al == 0Dh         ;****** Pattern Break **************
  1063.                 movzx   ebx,ah
  1064.                 and     bl,0f0h
  1065.                 shr     bl,4
  1066.                 imul    ebx,10
  1067.                 mov     bh,ah
  1068.                 and     bh,0Fh
  1069.                 add     bl,bh
  1070.                 dec     bl
  1071.                 mov     Current_Line,bl
  1072.                 inc     Song_position
  1073.                 mov     Current_tick,-1
  1074.                 ret
  1075.  
  1076.         .ELSEIF al == 0Fh       ; ******* Set the Speed **********
  1077.                  cmp ah,0
  1078.              jz ignSp
  1079.              cmp ah,31
  1080.                  ja ignSp
  1081.                  mov  speed,ah
  1082.            ignSp:
  1083.  
  1084.         .ENDIF
  1085.  
  1086.               mov   CurrentVoice_Args[ecx],ah       ; save the argument
  1087.  
  1088. no_effect_used:
  1089.                 mov     bl,[esi+2]       ;Extract instrument number into BL
  1090.             shr     bl,4
  1091.             mov     bh,[esi]
  1092.             and     bh,0f0h
  1093.                 or      bl,bh
  1094.                 sub     bl,1
  1095.                 jc   Skip_Note
  1096.  
  1097.                 mov     eax,[esi]              ; get Note value
  1098.                 xchg    ah,al
  1099.                 and     eax,00fffh
  1100.                 jz    Skip_Note
  1101.                 mov    VoiceNote[ecx*2],ax      ; save the note
  1102.                 call    Set_VoicePeriod
  1103.  
  1104.                 cmp Skip_volSet , True
  1105.                 je @f
  1106.                 mov     al,Instr_Volume[EBX]
  1107.                 mov     VoiceVolume[ecx],al     ; save the volume
  1108.                 call    Set_VoiceVolume
  1109.             @@:
  1110.                 call    Play_instrument          ; Start the voice playing
  1111. Skip_Note:
  1112.                add     esi,4
  1113.                inc     cl
  1114.                cmp     cl,Channels
  1115.                jb  playlineloop
  1116.  
  1117.                 ret
  1118. Play_Pattern_line ENDP
  1119.  
  1120.  
  1121.  
  1122.  
  1123.  
  1124.  
  1125.  
  1126. ;***** nul effect *************
  1127. Nul_Effect PROC
  1128.         ret
  1129. Nul_Effect ENDP
  1130.  
  1131. ;******** Slide the Volume *************
  1132. Slide_Volume PROC
  1133.         test    ah,0F0h
  1134.         jnz  UP
  1135.         and     ah,0Fh
  1136.         sub   VoiceVolume[ecx],ah
  1137.         jnc  @f
  1138.          mov   VoiceVolume[ecx],0
  1139. @@:     jmp exit1
  1140.  
  1141.  
  1142. UP:     shr     ah,4
  1143.         add   VoiceVolume[ecx],ah
  1144.         cmp   VoiceVolume[ecx],64
  1145.         jbe @f
  1146.          mov   VoiceVolume[ecx],64
  1147.   @@:
  1148.  
  1149. exit1:  mov     al,VoiceVolume[ecx]
  1150.         call    Set_VoiceVolume
  1151.         ret
  1152. Slide_Volume ENDP
  1153.  
  1154. ;****** Slide to Note ( dec/increase period )************
  1155. Slide_To_note PROC
  1156.         mov   bx,Destinamtion_note[ecx*2]  ; get Note value to slide towords
  1157.         movzx ax,ah
  1158.         cmp VoiceNote[ecx*2],bx
  1159.         jb up
  1160.         ja down
  1161.         ret
  1162. up:
  1163.         add VoiceNote[ecx*2],ax
  1164.         jmp exit1
  1165. down:
  1166.         sub VoiceNote[ecx*2],ax
  1167. exit1:
  1168.         mov     ax,VoiceNote[ecx*2]
  1169.         .If   ax > 856
  1170.           mov  ax,856
  1171.         .elseif ax < 113
  1172.           mov  ax,113
  1173.         .endif
  1174.         mov     VoiceNote[ecx*2],ax
  1175.         call    Set_VoicePeriod
  1176. Skip_Note:
  1177.         ret
  1178. Slide_To_note ENDP
  1179.  
  1180.  
  1181. END Start32
  1182.