home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / dos / prg / midas / s3m.asm < prev    next >
Assembly Source File  |  1994-08-06  |  66KB  |  2,926 lines

  1. ;*    S3M.ASM
  2. ;*
  3. ;* Scream Tracker 3 Module Player, v1.13
  4. ;*
  5. ;* Copyright 1994 Petteri Kangaslampi and Jarno Paananen
  6. ;*
  7. ;* This file is part of the MIDAS Sound System, and may only be
  8. ;* used, modified and distributed under the terms of the MIDAS
  9. ;* Sound System license, LICENSE.TXT. By continuing to use,
  10. ;* modify or distribute this file you indicate that you have
  11. ;* read the license and understand and accept it fully.
  12. ;*
  13.  
  14. IDEAL
  15. P386
  16. JUMPS
  17.  
  18. ; DEBUGGER = 1
  19.  
  20. INCLUDE "lang.inc"
  21. INCLUDE "errors.inc"
  22. INCLUDE "mglobals.inc"
  23. INCLUDE "s3m.inc"
  24. INCLUDE "mplayer.inc"
  25. INCLUDE "sdevice.inc"
  26. INCLUDE "ems.inc"
  27. INCLUDE "timer.inc"
  28.  
  29.  
  30.  
  31. DATASEG
  32.  
  33.  
  34.  
  35. module        DD    ?        ; pointer to module structure
  36. sdevice     DD    ?        ; pointer to current Sound Device
  37.  
  38. playCount    DB    ?        ; player speed counter
  39. speed        DB    ?        ; playing speed, default is 6
  40. tempo        DB    ?        ; playing BPM tempo
  41. masterVolume    DB    ?        ; master volume (0-64)
  42. loopStart    DW    ?        ; song loop start position
  43. loopEnd     DW    ?        ; song loop end position
  44. flags        DW    ?        ; module flags
  45. upperLimit    DW    ?        ; upper period value limit
  46. lowerLimit    DW    ?        ; lower period value limit
  47.  
  48. position    DW    ?        ; position in song
  49. row        DW    ?        ; row in pattern
  50. playOffset    DW    ?        ; offset to next pattern data in
  51.                     ; current pattern
  52. songLength    DW    ?        ; song length (number of positions)
  53. maxinst     DW    ?        ; maximum inst number
  54. numChans    DW    ?        ; number of channels
  55. firstSDChan    DW    ?        ; first Sound Device channel used
  56. chan        DW    ?        ; current channel number
  57. sdChan        DW    ?        ; current Sound Device channel number
  58.  
  59. oldOffset    DW    ?        ; old pattern data offset
  60.  
  61. loopRow     DW    ?        ; pattern loop row
  62. loopOffset    DW    ?        ; pattern loop position
  63. loopFlag    DB    ?        ; pattern loop flag
  64. loopCount    DB    ?        ; pattern loop counter
  65. delayCount    DB    ?        ; pattern delay counter
  66. delayFlag    DB    ?        ; pattern delay flag
  67. skipFlag    DB    ?        ; row skip flag
  68. pbFlag        DB    ?        ; pattern break flag
  69.  
  70. loopCnt     DB    ?        ; song loop counter
  71.  
  72. setFrame    DW    ?        ; "set frame" flag - 1 if song data
  73.                     ; is played, 0 if not
  74. rows        DW    ?        ; saved row number for GetInformation
  75. poss        DW    ?        ; saved position
  76. pats        DW    ?        ; saved pattern number
  77.  
  78. s3mMemPtr    DD    ?        ; temporary memory pointer used by
  79.                     ; some functions
  80.  
  81.  
  82. channels    s3mChannel MPCHANNELS DUP (?)    ; channel structures
  83.  
  84.  
  85.  
  86. IDATASEG
  87.  
  88.  
  89. ;/***************************************************************************\
  90. ;*     Scream Tracker 3 Module Player structure:
  91. ;\***************************************************************************/
  92.  
  93. mpS3M    ModulePlayer  < \
  94.     mpUnInitialized, \
  95.     5000, \
  96.     far ptr s3mIdentify, \
  97.     far ptr s3mInit, \
  98.     far ptr s3mClose, \
  99.     far ptr s3mLoadModule, \
  100.     far ptr s3mFreeModule, \
  101.     far ptr s3mPlayModule, \
  102.     far ptr s3mStopModule, \
  103.     far ptr s3mSetInterrupt, \
  104.     far ptr s3mRemoveInterrupt, \
  105.     far ptr s3mPlay, \
  106.     far ptr s3mSetPosition, \
  107.     far ptr s3mGetInformation,\
  108.     far ptr s3mSetMasterVolume >
  109.  
  110.  
  111.     ; sine table for vibrato:
  112. vibratoTable    DB    0,24,49,74,97,120,141,161
  113.         DB    180,197,212,224,235,244,250,253
  114.         DB    255,253,250,244,235,224,212,197
  115.         DB    180,161,141,120,97,74,49,24
  116.  
  117.     ; volume fade tables for Retrig Note:
  118. retrigTable1    DB    0,-1,-2,-4,-8,-16,0,0
  119.         DB    0,1,2,4,8,16,0,0
  120.  
  121. retrigTable2    DB    0,0,0,0,0,0,10,8
  122.         DB    0,0,0,0,0,0,24,32
  123.  
  124.  
  125.     ; period table for one octave:
  126. Periods     DW    1712,1616,1524,1440,1356,1280,1208,1140,1076,1016
  127.         DW    0960,0907
  128.  
  129.  
  130. LABEL    cmdNames    DWORD
  131.     DD    far ptr strNoCmd
  132.     DD    far ptr strSetSpeed
  133.     DD    far ptr strPosJump
  134.     DD    far ptr strPattBreak
  135.     DD    far ptr strVolSlide
  136.     DD    far ptr strSlideDown
  137.     DD    far ptr strSlideUp
  138.     DD    far ptr strTonePort
  139.     DD    far ptr strVibrato
  140.     DD    far ptr strTremor
  141.     DD    far ptr strArpeggio
  142.     DD    far ptr strVibVSlide
  143.     DD    far ptr strTPortVSlide
  144.     DD    far ptr strNoCmd
  145.     DD    far ptr strNoCmd
  146.     DD    far ptr strSampleOffs
  147.     DD    far ptr strNoCmd
  148.     DD    far ptr strRetrigNote
  149.     DD    far ptr strTremolo
  150.     DD    far ptr strSpecial
  151.     DD    far ptr strSetTempo
  152.     DD    far ptr strNoCmd
  153.     DD    far ptr strMasterVol
  154.     DD    far ptr strNoCmd
  155.     DD    far ptr strSetPanning
  156.     DD    far ptr strNoCmd
  157.     DD    far ptr strNoCmd
  158.  
  159. LABEL    scmdNames    DWORD
  160.     DD    far ptr strSetFilter
  161.     DD    far ptr strGlissCtrl
  162.     DD    far ptr strSetFinetune
  163.     DD    far ptr strVibWform
  164.     DD    far ptr strTremWform
  165.     DD    far ptr strNoCmd
  166.     DD    far ptr strNoCmd
  167.     DD    far ptr strNoCmd
  168.     DD    far ptr strSetPanning
  169.     DD    far ptr strNoCmd
  170.     DD    far ptr strStereoCtrl
  171.     DD    far ptr strPattLoop
  172.     DD    far ptr strNoteCut
  173.     DD    far ptr strNoteDelay
  174.     DD    far ptr strPattDelay
  175.     DD    far ptr strFunkRepeat
  176.  
  177.  
  178. strNoCmd    DB    0
  179.     ;        1234567890A
  180. strSlideUp    DB    "Slide Up",0
  181. strSlideDown    DB    "Slide Down",0
  182. strTonePort    DB    "Tone Porta",0
  183. strVibrato    DB    "Vibrato",0
  184. strTPortVSlide    DB    "TPrt+VolSld",0
  185. strVibVSlide    DB    "Vib+VolSld",0
  186. strTremor    DB    "Tremor",0
  187. strSampleOffs    DB    "Sample Offs",0
  188. strArpeggio    DB    "Arpeggio",0
  189. strVolSlide    DB    "VolumeSlide",0
  190. strPosJump    DB    "Pos. Jump",0
  191. strPattBreak    DB    "Patt. Break",0
  192. strSetSpeed    DB    "Set Speed",0
  193. srtSetVolume    DB    "Set Volume",0
  194. strRetrigNote    DB    "Retrig Note",0
  195. strTremolo    DB    "Tremolo",0
  196. strSpecial    DB    "Special Cmd",0
  197. strSetTempo    DB    "Set Tempo",0
  198. strMasterVol    DB    "Mast.Volume",0
  199.  
  200.         ;    01234567890A
  201. strSetFilter    DB    "Set Filter",0
  202. strGlissCtrl    DB    "Gliss. Ctrl",0
  203. strSetFinetune    DB    "SetFinetune",0
  204. strVibWform    DB    "Vib.Wavefrm",0
  205. strTremWform    DB    "Tre.Wavefrm",0
  206. strSetPanning    DB    "Set Panning",0
  207. strStereoCtrl    DB    "StereoCtrl",0
  208. strPattLoop    DB    "Patt.Loop",0
  209. strNoteCut    DB    "Note Cut",0
  210. strNoteDelay    DB    "Note Delay",0
  211. strPattDelay    DB    "Patt. Delay",0
  212. strFunkRepeat    DB    "FunkRepeat",0
  213.  
  214.  
  215.  
  216.  
  217. CODESEG
  218.  
  219.  
  220. ;/***************************************************************************\
  221. ;*
  222. ;* Function:    int s3mIdentify(uchar *header, int *recognized);
  223. ;*
  224. ;* Description: Checks if the header is a Scream Tracker 3 module header
  225. ;*
  226. ;* Input:    uchar *headeer        pointer to header, length MPHDRSIZE
  227. ;*        int *recognized     pointer to result variable
  228. ;*
  229. ;* Returns:    MIDAS error code.
  230. ;*        *recognized set to 1 if header is a Scream Tracker 3 module
  231. ;*        header, 0 if not
  232. ;*
  233. ;\***************************************************************************/
  234.  
  235. PROC    s3mIdentify    FAR    header : dword, recognized : dword
  236.  
  237.     les    bx,[header]        ; point es:bx to header
  238.     xor    ax,ax
  239.     cmp    [dword es:bx+s3mHeader.SCRM],"MRCS"     ; is SCRM field in
  240.     je    @@iss3m                 ; header "SCRM?"
  241.     xor    ax,ax            ; no - not an S3M
  242.     jmp    @@detd
  243.  
  244. @@iss3m:
  245.     mov    ax,1            ; yes - module is a S3M
  246.  
  247. @@detd:
  248.     les    bx,[recognized]     ; point es:bx to result
  249.     mov    [es:bx],ax        ; set *recognized to identification
  250.                     ; result
  251.     xor    ax,ax
  252.     ret
  253. ENDP
  254.  
  255.  
  256.  
  257.  
  258. ;/***************************************************************************\
  259. ;*
  260. ;* Function:    int s3mInit(SoundDevice *SD);
  261. ;*
  262. ;* Description: Initializes Scream Tracker 3 Module Player
  263. ;*
  264. ;* Input:    SoundDevice *SD     pointer to Sound Device to be used
  265. ;*                    for playing
  266. ;*
  267. ;* Returns:    MIDAS error code
  268. ;*
  269. ;\***************************************************************************/
  270.  
  271. PROC    s3mInit     FAR    SDev : dword
  272.  
  273.     mov    eax,[SDev]        ; store Sound Device pointer in
  274.     mov    [sdevice],eax        ; sdevice
  275.  
  276.     mov    [mpS3M.status],mpInitialized    ; Module Player is initialized
  277.  
  278.     xor    ax,ax            ; success
  279.  
  280.     ret
  281. ENDP
  282.  
  283.  
  284.  
  285.  
  286. ;/***************************************************************************\
  287. ;*
  288. ;* Function:    int s3mClose(void);
  289. ;*
  290. ;* Description: Uninitializes the Scream Tracker 3 Module Player
  291. ;*
  292. ;* Returns:    MIDAS error code
  293. ;*
  294. ;\***************************************************************************/
  295.  
  296. PROC    s3mClose FAR
  297.  
  298.     mov    [mpS3M.status],mpUnInitialized
  299.  
  300.     xor    ax,ax            ; success
  301.     ret
  302. ENDP
  303.  
  304.  
  305.  
  306.  
  307. ;/***************************************************************************\
  308. ;*
  309. ;* Function:     int s3mDetectChannels(mpModule *s3m, ushort *numChns);
  310. ;*
  311. ;* Description:  Detects the number of channels in a Scream Tracker 3 module
  312. ;*
  313. ;* Input:     mpModule *s3m         pointer to module structure
  314. ;*         ushort *numChns     pointer to channel number variable
  315. ;*
  316. ;* Returns:     MIDAS error code.
  317. ;*         Number of channels in module stored in *numChns.
  318. ;*
  319. ;\***************************************************************************/
  320.  
  321. PROC    s3mDetectChannels    FAR    s3m : dword, numChns : dword
  322. USES    si, di
  323. LOCAL    numPatts : word, PpattPtr : dword, pattNum : word, maxChan : word
  324.  
  325.     les    si,[s3m]        ; point es:si to module structure
  326.  
  327.     mov    ax,[es:si+mpModule.numPatts]    ; store number of patterns
  328.     mov    [numPatts],ax
  329.  
  330.     mov    eax,[es:si+mpModule.patterns]    ; store pattern pointer
  331.     mov    [PpattPtr],eax            ; pointer
  332.  
  333.     mov    [pattNum],0
  334.     mov    [maxChan],0        ; maximum channel used = 0
  335.  
  336. @@pattloop:
  337.     les    si,[s3m]        ; point es:si to module structure
  338.  
  339.     lgs    di,[es:si+mpModule.pattEMS]    ; point gs:di to pattern EMS
  340.     mov    bx,[pattNum]            ; flags
  341.     cmp    [byte gs:di+bx],0        ; is pattern in EMS?
  342.     lgs    di,[PpattPtr]            ; point gs:di to pattern ptr
  343.     je    @@noEMS
  344.  
  345.     cmp    [dword gs:di],0
  346.     je    @@nextptrn
  347.  
  348.     ; map pattern to conventional memory:
  349.     call    emsMap LANG, [dword gs:di], seg s3mMemPtr offset s3mMemPtr
  350.     test    ax,ax
  351.     jnz    @@err
  352.  
  353.     les    si,[s3mMemPtr]        ; point es:si to conventional memory
  354.     jmp    @@dataok        ; block
  355.  
  356. @@noEMS:
  357.     cmp    [dword gs:di],0
  358.     je    @@nextptrn
  359.     les    si,[gs:di]        ; no EMS - point es:si to pattern
  360.  
  361. @@dataok:
  362.     add    si,2            ; skip pattern length
  363.     mov    cx,64            ; cx = row counter
  364.  
  365. @@rowloop:
  366.     mov    al,[es:si]        ; read row flag byte
  367.     inc    si
  368.     test    al,al            ; zero?
  369.     jz    @@nextrow        ; if is, move to next row
  370.  
  371.     mov    bl,al
  372.     and    bx,01Fh         ; bx = channel number
  373.  
  374.     test    al,32            ; is bit 5 of flag byte 1?
  375.     jz    @@nonote        ; if not, there is no note/instrument
  376.  
  377.     cmp    [byte es:si],255
  378.     je    @@noted         ; skip empty or release notes
  379.     cmp    [byte es:si],254
  380.     je    @@noted
  381.  
  382.     cmp    [byte es:si+1],255    ; skip empty instruments
  383.     je    @@noted
  384.  
  385.     cmp    [maxChan],bx        ; Channel is used. If channel number
  386.     jae    @@noted         ; > maximum channel number used,
  387.     mov    [maxChan],bx        ; set maximum channel number
  388.  
  389. @@noted:
  390.     add    si,2            ; note and instrument processed
  391.  
  392.  
  393. @@nonote:
  394.     test    al,64            ; if bit 6 is 1, there is a volume
  395.     jz    @@novol         ; field
  396.     inc    si            ; skip volume
  397.  
  398.  
  399. @@novol:
  400.     test    al,128            ; if bit 7 is 1, there is a command
  401.     jz    @@rowloop        ; field
  402.  
  403.     cmp    [byte es:si],1        ; skip empty commands
  404.     jb    @@cmddone
  405.     cmp    [byte es:si],'Z'-'@'    ; skip invalid commands
  406.     ja    @@cmddone
  407.  
  408.     cmp    [maxChan],bx        ; Channel is used. If channel number
  409.     jae    @@cmddone        ; > maximum channel number used,
  410.     mov    [maxChan],bx        ; set maximum channel number
  411.  
  412. @@cmddone:
  413.     add    si,2            ; command and infobyte processed
  414.     jmp    @@rowloop        ; next flag byte
  415.  
  416. @@nextrow:
  417.     loop    @@rowloop        ; go to next row
  418.  
  419.  
  420. @@nextptrn:
  421.     add    [word PpattPtr],4    ; pointer to next pattern pointer
  422.     inc    [pattNum]        ; next pattern
  423.     mov    ax,[pattNum]
  424.     cmp    ax,[numPatts]        ; are there more patterns?
  425.     jb    @@pattloop
  426.  
  427.  
  428.     les    si,[s3m]        ; point es:si to module structure
  429.  
  430.     xor    bx,bx            ; channel number = 0
  431.     mov    cx,MPCHANNELS        ; search all channels
  432.     xor    dx,dx            ; max channel number = 0
  433.  
  434.     ; now find the maximum channel number which has data and is used
  435.     ; according to the mpModule channel settings table:
  436.  
  437. @@cloop:
  438.     cmp    [maxChan],bx        ; is channel number too big?
  439.     jb    @@cdone
  440.  
  441.     ; this channel has data - now check if it is turned on:
  442.     cmp    [es:si+bx+mpModule.chanSettings],0
  443.     jz    @@notused
  444.  
  445.     mov    dx,bx            ; channel has data and is turned on
  446.  
  447. @@notused:
  448.     inc    bx            ; next channel
  449.     loop    @@cloop
  450.  
  451. @@cdone:
  452.     ; dx contains the actual number of channels
  453.     les    bx,[numChns]        ; store number of channels in
  454.     inc    dx            ; *numChns
  455.     mov    [es:bx],dx
  456.  
  457.     xor    ax,ax            ; success
  458.     jmp    @@done
  459.  
  460. @@err:
  461.     ERROR    ID_s3mDetectChannels
  462.  
  463. @@done:
  464.     ret
  465. ENDP
  466.  
  467.  
  468.  
  469.  
  470. ;/***************************************************************************\
  471. ;*
  472. ;* Function:     int s3mFindUsedInsts(mpModule *s3m, ushort *used);
  473. ;*
  474. ;* Description:  Detects used instruments in a Scream Tracker 3 module
  475. ;*
  476. ;* Input:     mpModule *s3m         pointer to module structure
  477. ;*         uchar      *used      pointer to sample array
  478. ;*
  479. ;* Returns:     MIDAS error code.
  480. ;*
  481. ;\***************************************************************************/
  482.  
  483. PROC    s3mFindUsedInsts    FAR    s3m : dword, used : dword
  484. USES    si, di
  485. LOCAL    numPatts : word, PpattPtr : dword, pattNum : word, maxi : word
  486.  
  487.     les    si,[s3m]        ; point es:si to module structure
  488.  
  489.     mov    ax,[es:si+mpModule.numPatts]    ; store number of patterns
  490.     mov    [numPatts],ax
  491.     mov    ax,[es:si+mpModule.numInsts]
  492.     mov    [maxi],ax
  493.  
  494.     mov    cx,ax                ; Clear array (mark unused)
  495.     xor    al,al
  496.     push    es
  497.     les    di,[used]
  498.     rep    stosb
  499.     pop    es
  500.  
  501.     mov    eax,[es:si+mpModule.patterns]    ; store pattern pointer
  502.     mov    [PpattPtr],eax            ; pointer
  503.  
  504.     mov    [pattNum],0
  505.  
  506. @@pattloop:
  507.     les    si,[s3m]        ; point es:si to module structure
  508.  
  509.     lgs    di,[es:si+mpModule.pattEMS]    ; point gs:di to pattern EMS
  510.     mov    bx,[pattNum]            ; flags
  511.     cmp    [byte gs:di+bx],0        ; is pattern in EMS?
  512.     lgs    di,[PpattPtr]            ; point gs:di to pattern ptr
  513.     je    @@noEMS
  514.  
  515.     ; map pattern to conventional memory:
  516.     cmp    [dword gs:di],0
  517.     je    @@nextptrn
  518.  
  519.     call    emsMap LANG, [dword gs:di], seg s3mMemPtr offset s3mMemPtr
  520.     test    ax,ax
  521.     jnz    @@err
  522.  
  523.     les    si,[s3mMemPtr]        ; point es:si to conventional memory
  524.     jmp    @@dataok        ; block
  525.  
  526. @@noEMS:
  527.     cmp    [dword gs:di],0
  528.     je    @@nextptrn
  529.     les    si,[gs:di]        ; no EMS - point es:si to pattern
  530.  
  531. @@dataok:
  532.     add    si,2            ; skip pattern length
  533.     mov    cx,64            ; cx = row counter
  534.     lgs    di,[used]
  535.  
  536. @@rowloop:
  537.     mov    al,[es:si]        ; read row flag byte
  538.     inc    si
  539.     test    al,al            ; zero?
  540.     jz    @@nextrow        ; if is, move to next row
  541.  
  542.     mov    bl,al
  543.     and    bx,01Fh         ; bx = channel number
  544.  
  545.     test    al,32            ; is bit 5 of flag byte 1?
  546.     jz    @@nonote        ; if not, there is no note/instrument
  547.  
  548.     movzx    bx,[es:si+1]
  549.     test    bl,80h
  550.     jnz    @@empty
  551.     test    bl,bl
  552.     jz    @@empty
  553.  
  554.     cmp    bx,[maxi]
  555.     ja    @@empty         ; Insane instrument
  556.  
  557.     dec    bx
  558.     mov    [byte gs:di+bx],1    ; Mark used
  559. @@empty:
  560.     add    si,2            ; note and instrument processed
  561.  
  562. @@nonote:
  563.     test    al,64            ; if bit 6 is 1, there is a volume
  564.     jz    @@novol         ; field
  565.     inc    si            ; skip volume
  566.  
  567. @@novol:
  568.     test    al,128            ; if bit 7 is 1, there is a command
  569.     jz    @@rowloop        ; field
  570.     add    si,2            ; command and infobyte processed
  571.     jmp    @@rowloop        ; next flag byte
  572.  
  573. @@nextrow:
  574.     loop    @@rowloop        ; go to next row
  575.  
  576. @@nextptrn:
  577.     add    [word PpattPtr],4    ; pointer to next pattern pointer
  578.     inc    [pattNum]        ; next pattern
  579.     mov    ax,[pattNum]
  580.     cmp    ax,[numPatts]        ; are there more patterns?
  581.     jb    @@pattloop
  582.     xor    ax,ax
  583.     jmp    @@quit
  584.  
  585. @@err:    ERROR    ID_s3mFindUsedInsts
  586. @@quit: ret
  587. ENDP
  588.  
  589. ;/***************************************************************************\
  590. ;*
  591. ;* Function:    int s3mPlayModule(mpModule *module, ushort firstSDChannel,
  592. ;*            ushort numChannels, ushort loopStart, ushort loopEnd);
  593. ;*
  594. ;*
  595. ;* Description: Starts playing a module
  596. ;*
  597. ;* Input:    mpModule *module    pointer to the module to be played
  598. ;*        ushort firstSDChannel    first Sound Device channel to use
  599. ;*        ushort numChannels    number of channels
  600. ;*        ushort loopStart    song loop start (0 = beginning)
  601. ;*        ushort loopEnd        song loop end (use 65535 for whole
  602. ;*                    song if length is unknown)
  603. ;*
  604. ;* Returns:    MIDAS error code
  605. ;*
  606. ;\***************************************************************************/
  607.  
  608. PROC    s3mPlayModule    FAR    ms3m : dword, firstSDChannel : word, \
  609.                 numChannels : word, lpStart : word, \
  610.                 lpEnd : word
  611. USES    si, di
  612.  
  613.     mov    eax,[ms3m]        ; store module pointer in module
  614.     mov    [module],eax
  615.     les    si,[module]        ; point es:si to module structure
  616.  
  617.     mov    ax,[es:si+mpModule.numInsts]    ; get amount of insts from module
  618.     mov    [maxinst],ax            ; and store it
  619.  
  620.     mov    ax,[es:si+mpModule.songLength]    ; get song length from module
  621.     mov    [songLength],ax         ; and store it
  622.  
  623.     mov    ax,[firstSDChannel]    ; store first SD channel number
  624.     mov    [firstSDChan],ax
  625.     mov    ax,[numChannels]    ; store number of channels
  626.     mov    [numChans],ax
  627.  
  628.     mov    al,[es:si+mpModule.speed]    ; get module initial speed
  629.     mov    [speed],al        ; set speed to initial speed
  630.  
  631.     mov    ax,[es:si+mpModule.flags]    ; store module flags
  632.     mov    [flags],ax
  633.  
  634.     mov    bx,7FFFh        ; bx = lower period limit
  635.     mov    cx,64            ; cx = upper period limit
  636.  
  637.     test    ax,10h            ; is "amigalimits"-flag 1?
  638.     jz    @@st3limits
  639.  
  640.     mov    bx,856*4        ; yes, set Amiga period limits
  641.     mov    cx,113*4
  642.  
  643. @@st3limits:
  644.     mov    [upperLimit],bx     ; store period limits
  645.     mov    [lowerLimit],cx
  646.  
  647.     movzx    ax,[es:si+mpModule.tempo]    ; get initial tempo
  648.     mov    [tempo],al            ; set BPM tempo
  649.  
  650.     lgs    di,[sdevice]
  651.     mov    bx,40
  652.     mul    bx            ; BPM*40 = rate in 100*Hz
  653.     mov    [mpS3M.updRate],ax
  654.     push    es gs            ; set Sound Device update rate:
  655.     call    [gs:di+SoundDevice.SetUpdRate] LANG, ax
  656.     pop    gs es
  657.     test    ax,ax
  658.     jnz    @@err
  659.  
  660.     mov    ax,[es:si+mpModule.songLength]    ; store song length
  661.     mov    [songLength],ax
  662.  
  663.     mov    [chan],0
  664.  
  665.     ; set initial panning values to channels:
  666. @@panloop:
  667.     mov    bx,[chan]
  668.     movsx    ax,[es:si+bx+mpModule.chanSettings]
  669.     add    bx,[firstSDChan]        ; bx = Sound Device channel
  670.                         ; number
  671.  
  672.     ; set Sound Device panning:
  673.     push    es gs
  674.     call    [gs:di+SoundDevice.SetPanning] LANG, bx, ax
  675.     pop    gs es
  676.     test    ax,ax
  677.     jnz    @@err
  678.  
  679.     inc    [chan]                ; next channel
  680.     mov    ax,[chan]
  681.     cmp    ax,[numChans]
  682.     jb    @@panloop
  683.  
  684.  
  685.     ; initialize player internal variables:
  686.     mov    [playCount],0
  687.     mov    [masterVolume],64
  688.     mov    [playOffset],2        ; skip pattern length word
  689.     mov    [row],0
  690.     mov    [loopFlag],0
  691.     mov    [loopCount],0
  692.     mov    [delayFlag],0
  693.     mov    [delayCount],0
  694.     mov    [pbFlag],0
  695.     mov    [loopCnt],0
  696.     mov    [skipFlag],0
  697.  
  698.     mov    ax,[lpStart]        ; get loop start position
  699.     mov    [loopStart],ax        ; store loop start position
  700.     mov    [position],ax        ; set position to loop start
  701.     mov    ax,[lpEnd]        ; get loop end position
  702.     mov    [loopEnd],ax        ; store loop end position
  703.  
  704.  
  705.     ; clear player channel structures to all zeros:
  706.     mov    ax,ds
  707.     mov    es,ax
  708.     mov    di,offset channels
  709.     mov    cx,MPCHANNELS * SIZE s3mChannel
  710.     xor    al,al
  711.     cld
  712.     rep    stosb
  713.  
  714.     mov    [mpS3M.status],mpPlaying
  715.  
  716.     xor    ax,ax            ; success
  717.     jmp    @@done
  718.  
  719. @@err:
  720.     ERROR    ID_s3mPlayModule
  721.  
  722. @@done:
  723.     ret
  724.  
  725. ENDP
  726.  
  727.  
  728.  
  729.  
  730. ;/***************************************************************************\
  731. ;*
  732. ;* Function:    int s3mStopModule(void);
  733. ;*
  734. ;* Description: Stops playing a module
  735. ;*
  736. ;* Returns:    MIDAS error code
  737. ;*
  738. ;\***************************************************************************/
  739.  
  740. PROC    s3mStopModule    FAR
  741.  
  742.     mov    [module],0        ; point module to NULL for safety
  743.     mov    [mpS3M.status],mpStopped
  744.  
  745.     xor    ax,ax            ; success
  746.     ret
  747. ENDP
  748.  
  749.  
  750.  
  751.  
  752. ;/***************************************************************************\
  753. ;*
  754. ;* Function:    int s3mSetInterrupt(void);
  755. ;*
  756. ;* Description: Starts playing the module using TempoTimer
  757. ;*
  758. ;* Returns:    MIDAS error code
  759. ;*
  760. ;\***************************************************************************/
  761.  
  762. PROC    s3mSetInterrupt     FAR
  763.  
  764.     ; start playing with the TempoTimer:
  765.     call    tmrPlay LANG, seg s3mPlay offset s3mPlay, [sdevice]
  766.     test    ax,ax
  767.     jnz    @@err
  768.  
  769.     movzx    ax,[tempo]
  770.     mov    bx,40            ; BPM * 40 = playing rate in 100*Hz
  771.     mul    bx
  772.     call    tmrSetUpdRate LANG, ax    ; set timer update rate
  773.     test    ax,ax
  774.     jnz    @@err
  775.  
  776.     xor    ax,ax            ; success
  777.     jmp    @@done
  778.  
  779. @@err:
  780.     ERROR    ID_s3mSetInterrupt
  781.  
  782. @@done:
  783.     ret
  784. ENDP
  785.  
  786.  
  787.  
  788.  
  789. ;/***************************************************************************\
  790. ;*
  791. ;* Function:    int s3mRemoveInterrupt(void);
  792. ;*
  793. ;* Description: Stops playing with the TempoTimer
  794. ;*
  795. ;* Returns:    MIDAS error code
  796. ;*
  797. ;\***************************************************************************/
  798.  
  799. PROC    s3mRemoveInterrupt    FAR
  800.  
  801.     call    tmrStop LANG        ; stop playing the module with timer
  802.     test    ax,ax
  803.     jnz    @@err
  804.  
  805.     xor    ax,ax            ; success
  806.     jmp    @@done
  807.  
  808. @@err:
  809.     ERROR    ID_s3mRemoveInterrupt
  810.  
  811. @@done:
  812.     ret
  813. ENDP
  814.  
  815.  
  816.  
  817.  
  818. ;/***************************************************************************\
  819. ;*
  820. ;* Function:    int s3mPlay(void);
  821. ;*
  822. ;* Description: Plays one "frame" of the module. Usually called from
  823. ;*        the timer.
  824. ;*
  825. ;* Returns:    MIDAS error code
  826. ;*
  827. ;\***************************************************************************/
  828.  
  829. PROC    s3mPlay     FAR
  830. USES    di, si
  831.  
  832.     inc    [playCount]        ; increment player counter
  833.     mov    al,[speed]        ; if player counter is equal to the
  834.     cmp    [playCount],al        ; speed, it's time to play the song
  835.     jne    @@noplay        ; data.
  836.  
  837.     call    s3mPlaySong        ; play one row of the song data
  838.     test    ax,ax
  839.     jnz    @@err
  840.     jmp    @@ok
  841.  
  842. @@noplay:
  843.     ; Song data is not played - just process the continuous commands
  844.     call    s3mRunCommands
  845.     test    ax,ax
  846.     jnz    @@err
  847.  
  848. @@ok:    xor    ax,ax            ; success
  849.     jmp    @@done
  850.  
  851. @@err:
  852.     ERROR    ID_s3mPlay
  853.  
  854. @@done:
  855.     ret
  856. ENDP
  857.  
  858.  
  859.  
  860.  
  861. ;/***************************************************************************\
  862. ;*
  863. ;* Function:    s3mRunCommands
  864. ;*
  865. ;* Description: Processes the continuous commands
  866. ;*
  867. ;* Returns:    MIDAS error code in ax
  868. ;*
  869. ;\***************************************************************************/
  870.  
  871. PROC NOLANGUAGE s3mRunCommands    NEAR
  872.  
  873.     mov    [chan],0        ; channel number = 0
  874.     mov    ax,[firstSDChan]    ; start from first Sound Device
  875.     mov    [sdChan],ax        ; channel
  876.  
  877.     mov    di,offset channels    ; point ds:di to channel structures
  878.     lgs    si,[sdevice]        ; point gs:si to Sound Device
  879.  
  880. @@chanloop:
  881.     test    [di+s3mChannel.flags],128    ; is there a command for
  882.     jz    @@nocmd             ; this channel?
  883.  
  884.     movzx    bx,[di+s3mChannel.cmd]    ; bx = command number
  885.     cmp    bx,27            ; is command number valid? (<=27)
  886.     ja    @@nocmd
  887.  
  888.     add    bx,bx            ; bx = index to command offset table
  889.     movzx    ax,[di+s3mChannel.preinfo]    ; ax = command infobyte
  890.     call    [word contCommands+bx]    ; process the command
  891.     test    ax,ax            ; error?
  892.     jnz    @@done            ; if yes, pass the error code on
  893.  
  894. @@nocmd:
  895.     add    di,size s3mChannel    ; next channel
  896.     inc    [chan]
  897.     inc    [sdChan]
  898.  
  899.     mov    ax,[chan]
  900.     cmp    ax,[numChans]
  901.     jb    @@chanloop
  902.  
  903.     call    s3mUpdBars        ; update volume bars
  904.  
  905. @@done:
  906. IFDEF DEBUGGER
  907.     test    ax,ax
  908.     jz    @@retu
  909.  
  910.     int 3
  911.  
  912. @@retu:
  913. ENDIF
  914.     ret
  915. ENDP
  916.  
  917.  
  918.  
  919.  
  920. ;/***************************************************************************\
  921. ;*
  922. ;* Function:    s3mPlaySong
  923. ;*
  924. ;* Description: Plays one row of song data
  925. ;*
  926. ;* Returns:    MIDAS error code in ax
  927. ;*
  928. ;\***************************************************************************/
  929.  
  930. PROC    s3mPlaySong    NEAR
  931. LOCAL    pattPtr : dword
  932.  
  933.     mov    [playCount],0        ; reset player counter
  934.  
  935.     cmp    [delayCount],0
  936.     je    @@nodelay
  937.  
  938.     ; pattern delay counter is non-zero. Decrement it and process
  939.     ; continuous commands.
  940.  
  941.     dec    [delayCount]
  942.     call    s3mRunCommands
  943.     ; pass possible error code on
  944.     jmp    @@done
  945.  
  946.  
  947. @@nodelay:
  948.     mov    [delayFlag],0            ; Pattern Delay not active
  949.  
  950.     ; clear flags from all channels:
  951.  
  952.     mov    di,offset channels
  953.     mov    cx,[numChans]
  954. @@cllp: mov    [di+s3mChannel.flags],0
  955.     add    di,SIZE s3mChannel
  956.     loop    @@cllp
  957.  
  958.     cmp    [skipFlag],0
  959.     je    @@noskip
  960.  
  961.     mov    [skipFlag],0
  962.     call    SkipRows
  963.     test    ax,ax
  964.     jne    @@done
  965.  
  966. @@noskip:
  967.     les    si,[module]        ; point es:si to module structure
  968.  
  969.     mov    bx,[position]
  970.     lgs    di,[es:si+mpModule.orders]    ; point gs:di to orders
  971.     movzx    cx,[gs:di+bx]            ; cx = current pattern number
  972.     shl    cx,2
  973.  
  974.     lgs    di,[es:si+mpModule.patterns]    ; point gs:di to current
  975.     add    di,cx                ; pattern pointer
  976.  
  977.     push    gs di
  978.     lgs    di,[es:si+mpModule.pattEMS]    ; point gs:di pattern EMS
  979.     shr    cx,2                ; flags
  980.     mov    bx,cx
  981.     cmp    [byte gs:di+bx],0        ; is current pattern in EMS?
  982.     pop    di gs
  983.     je    @@noEMS
  984.  
  985.     ; map pattern data to conventional memory:
  986.     call    emsMap LANG, [dword gs:di], seg s3mMemPtr offset s3mMemPtr
  987.     test    ax,ax
  988.     jnz    @@done
  989.  
  990.     mov    eax,[s3mMemPtr]     ; store pattern data pointer
  991.     mov    [pattPtr],eax
  992.     jmp    @@dataok
  993.  
  994. @@noEMS:
  995.     mov    eax,[gs:di]        ; store pattern data pointer
  996.     mov    [pattPtr],eax
  997.  
  998. @@dataok:
  999.     les    si,[pattPtr]        ; point es:si to pattern data
  1000.     mov    ax,[playOffset]     ; store current playing offset in
  1001.     mov    [oldOffset],ax        ; oldOffset
  1002.     add    si,ax            ; point es:si to current row data
  1003.  
  1004.  
  1005. @@dataloop:
  1006.     mov    al,[es:si]        ; al = current channel flag byte
  1007.     inc    si
  1008.     test    al,al            ; is flag byte zero?
  1009.     jz    @@rowend        ; if is, end of row
  1010.  
  1011.     xor    bx,bx
  1012.     mov    bl,al
  1013.     and    bl,31            ; bx = current channel number
  1014.  
  1015.     mov    di,offset channels
  1016.     imul    bx,bx,size s3mChannel    ; point ds:di to current channel
  1017.     add    di,bx            ; structure
  1018.  
  1019.     mov    [di+s3mChannel.flags],al    ; store flag byte
  1020.  
  1021.     test    al,32            ; if bit 5 of flag is 1, there is a
  1022.     jz    @@nonote        ; note or instrument
  1023.  
  1024.     mov    cl,[es:si]            ; get note number
  1025.     mov    [di+s3mChannel.note],cl     ; and store it
  1026.     inc    si
  1027.     mov    cl,[es:si]            ; get instrument number
  1028.     mov    [di+s3mChannel.inst],cl     ; and store it
  1029.     inc    si
  1030.  
  1031. @@nonote:
  1032.     test    al,64            ; if bit 6 of flag is 1, there is a
  1033.     jz    @@novol         ; volume change byte
  1034.  
  1035.     mov    cl,[es:si]        ; get volume change byte
  1036.     mov    [di+s3mChannel.vol],cl    ; and store it
  1037.     inc    si
  1038.  
  1039. @@novol:
  1040.     test    al,128            ; if bit 7 of flag 1 is, there is a
  1041.     jz    @@nocmd         ; command
  1042.  
  1043.     mov    cl,[es:si]        ; get command number
  1044.     mov    [di+s3mChannel.cmd],cl    ; and store it
  1045.     inc    si
  1046.     mov    cl,[es:si]        ; get command infobyte
  1047.     mov    [di+s3mChannel.info],cl ; and store it
  1048.     inc    si
  1049.  
  1050. @@nocmd:
  1051.     jmp    @@dataloop        ; get next flag byte and datas
  1052.  
  1053.  
  1054. @@rowend:
  1055.     sub    si,[word pattPtr]    ; play offset = si - pattern start
  1056.     mov    [playOffset],si     ; offset
  1057.  
  1058.     les    si,[module]        ; save values for getInformation
  1059.     call    s3mSave
  1060.     test    ax,ax
  1061.     jnz    @@done
  1062.  
  1063.  
  1064.  
  1065.     ; process possible new values on all channels:
  1066.  
  1067.     mov    [chan],0
  1068.     mov    ax,[firstSDChan]
  1069.     mov    [sdChan],ax
  1070.  
  1071.     mov    di,offset channels    ; point ds:di to channel structure
  1072.     lgs    si,[sdevice]        ; point gs:si to Sound Device
  1073.  
  1074. @@chanloop:
  1075.     test    [di+s3mChannel.flags],32    ; if flag bit 5 is 1, there
  1076.     jz    @@nonewnote            ; is a new note or instrument
  1077.  
  1078.     movzx    bx,[di+s3mChannel.inst]     ; ebx = new instrument number
  1079.     or    bl,bl
  1080.     jz    @@nonewinst
  1081.     js    @@nonewinst
  1082.     cmp    bx,[maxinst]
  1083.     ja    @@nonewinst
  1084.  
  1085.     mov    [di+s3mChannel.sample],bl    ; store new instrument number
  1086.  
  1087.     push    si
  1088.     les    si,[module]
  1089.     les    si,[es:si+mpModule.insts]    ; point es:si to new
  1090.     dec    bx                ; instrument structure
  1091.     imul    bx,bx,SIZE mpInstrument
  1092.     add    si,bx
  1093.  
  1094.     mov    al,[es:si+mpInstrument.volume]        ; al = inst volume
  1095.     mov    bx,[es:si+mpInstrument.sdInstHandle]    ; bx = inst SD handle
  1096.     pop    si
  1097.  
  1098.     mov    [di+s3mChannel.volume],al    ; set instrument volume
  1099.     or    [di+s3mChannel.status],1    ; volume changed
  1100.  
  1101.     ; set instrument to Sound Device:
  1102.     push    gs
  1103.     call    [gs:si+SoundDevice.SetInstrument] LANG, [sdChan], bx
  1104.     pop    gs
  1105.     test    ax,ax
  1106.     jnz    @@done
  1107.  
  1108.     cmp    [masterVolume],64        ; is master volume != 64?
  1109.     je    @@nonewinst
  1110.  
  1111.     call    SetVolume
  1112.     test    ax,ax
  1113.     jnz    @@done
  1114.  
  1115.  
  1116. @@nonewinst:
  1117.     mov    bl,[di+s3mChannel.note]     ; bl = new note number
  1118.     cmp    bl,255                ; is note empty note?
  1119.     je    @@nonewnote
  1120.  
  1121.     mov    [di+s3mChannel.snote],bl    ; save new note number
  1122.  
  1123.     cmp    bl,254                ; is note key off?
  1124.     je    @@keyoff
  1125.  
  1126.     test    [di+s3mChannel.flags],128    ; is there a command?
  1127.     jz    @@nondelay
  1128.     cmp    [di+s3mChannel.cmd],"S"-"@"     ; is command S-command?
  1129.     jne    @@nondelay
  1130.     mov    al,[di+s3mChannel.info]
  1131.     and    al,0F0h             ; is command Note Delay?
  1132.     cmp    al,0D0h
  1133.     je    @@nonewnote            ; if is, do not set note
  1134.  
  1135.  
  1136. @@nondelay:
  1137.     mov    cl,bl                ; cl = new note octave
  1138.     shr    cl,4
  1139.  
  1140.     ; Calculate sampling rate that corresponds to new note. A crazy
  1141.     ; method, but this is what the Scream Tracker 3 documentation
  1142.     ; says:
  1143.  
  1144. ;              8363 * 16 * ( period(NOTE) >> octave(NOTE) )
  1145. ;     note_st3period = --------------------------------------------
  1146. ;              middle_c_finetunevalue(INSTRUMENT)
  1147. ;
  1148. ;     note_amigaperiod = note_st3period / 4
  1149. ;
  1150. ;     note_herz=14317056 / note_st3period
  1151.  
  1152.  
  1153.     and    bx,0Fh
  1154.     add    bx,bx
  1155.     movzx    eax,[Periods+bx]        ; eax = new note period
  1156.     imul    eax,eax,8363*16         ; eax = 8363 * 16 * period
  1157.     shr    eax,cl                ;    / 2^oct
  1158.  
  1159.     movzx    bx,[di+s3mChannel.sample]
  1160.     test    bx,bx
  1161.     jz    @@nonewnote
  1162.  
  1163.     push    si
  1164.     les    si,[module]
  1165.     les    si,[es:si+mpModule.insts]
  1166.     dec    bx                ; point es:si to current
  1167.     imul    bx,bx,SIZE mpInstrument     ; instrument structure
  1168.     add    si,bx
  1169.  
  1170.     mov    ebx,[es:si+mpInstrument.c2Rate] ; ebx = instrument C4 sampling
  1171.     pop    si                ; rate
  1172.  
  1173.     test    ebx,ebx             ; is c2Rate zero?
  1174.     jz    @@nonewnote
  1175.     cdq                    ; eax = period = 8366 * 16
  1176.     idiv    ebx                ;   * period / 2^oct / c2Rate
  1177.  
  1178.     test    [di+s3mChannel.flags],128    ; is there a command?
  1179.     jz    @@ncd
  1180.     cmp    [di+s3mChannel.cmd],7        ; is command a Tone Portamento
  1181.     je    @@tport
  1182.     cmp    [di+s3mChannel.cmd],12        ; or Tone Portamento + VSlide?
  1183.     je    @@tport
  1184.  
  1185. @@ncd:    mov    [di+s3mChannel.vibpos],0    ; clear vibrato position
  1186.     or    [di+s3mChannel.status],3    ; note has been played
  1187.  
  1188.     mov    bl,[di+s3mChannel.cmd]        ; bl = command
  1189.  
  1190.     cmp    bl,"I"-"@"                      ; is current command tremor?
  1191.     je    @@notremor
  1192.  
  1193.     mov    [di+s3mChannel.trefl],0     ; no - clear tremor flag
  1194.     mov    [di+s3mChannel.trecnt],0    ; and tremor counter
  1195.  
  1196. @@notremor:
  1197.     cmp    bl,"Q"-"@"                      ; is current command retrig?
  1198.     je    @@noretrig
  1199.  
  1200.     mov    [di+s3mChannel.retrigc],0    ; no - clear retrig counter
  1201.  
  1202.  
  1203. @@noretrig:
  1204.     mov    [di+s3mChannel.period],ax    ; store current period number
  1205.  
  1206.     mov    ebx,eax             ; ebx = period
  1207.     test    ebx,ebx             ; do not set note if period
  1208.     jz    @@nonewnote            ; is zero
  1209.  
  1210.     mov    eax,14317056
  1211.     cdq                ; ebx = note sampling rate in Hz
  1212.     idiv    ebx
  1213.     mov    ebx,eax
  1214.  
  1215.     test    [di+s3mChannel.flags],128    ; is there a command?
  1216.     jz    @@nosoffs
  1217.     cmp    [di+s3mChannel.cmd],"O"-"@"     ; is command Sample Offset?
  1218.     je    @@smpoffset
  1219.  
  1220. @@nosoffs:
  1221.     ; start playing current note with Sound Device:
  1222.     push    gs
  1223.     call    [gs:si+SoundDevice.PlaySound] LANG, [sdChan], ebx
  1224.     pop    gs
  1225.     test    ax,ax
  1226.     jnz    @@done
  1227.     jmp    @@notedone
  1228.  
  1229.  
  1230. @@smpoffset:
  1231.     ; current command is Sample offset. Set the sampling rate:
  1232.     push    gs
  1233.     call    [gs:si+SoundDevice.SetRate] LANG, [sdChan], ebx
  1234.     pop    gs
  1235.     test    ax,ax
  1236.     jnz    @@done
  1237.  
  1238.     xor    bl,bl
  1239.     mov    bh,[di+s3mChannel.info]     ; bx = sample offset position
  1240.     test    bh,bh                ; is infobyte zero?
  1241.     jnz    @@sozero
  1242.     mov    bh,[di+s3mChannel.preinfo]    ; use previous infobyte
  1243.  
  1244. @@sozero:
  1245.     ; set playing position:
  1246.     push    gs
  1247.     call    [gs:si+SoundDevice.SetPosition] LANG, [sdChan], bx
  1248.     pop    gs
  1249.     test    ax,ax
  1250.     jnz    @@done
  1251.     jmp    @@notedone
  1252.  
  1253.  
  1254. @@tport:
  1255.     ; current command is Tone Portamento - store period as Tone Portamento
  1256.     ; destination:
  1257.     mov    [di+s3mChannel.toperi],ax
  1258.     jmp    @@nonewnote
  1259.  
  1260.  
  1261. @@keyoff:
  1262.     ; note is key off - stop playing sound:
  1263.     push    gs
  1264.     call    [gs:si+SoundDevice.StopSound] LANG, [sdChan]
  1265.     pop    gs
  1266.  
  1267. @@nonewnote:
  1268.     test    [di+s3mChannel.flags],128    ; is there a command?
  1269.     jz    @@resper
  1270.     cmp    [di+s3mChannel.cmd],'H'-'@'     ; is command vibrato?
  1271.     je    @@perdone
  1272.  
  1273. @@resper:
  1274.     ; no new note - reset channel period:
  1275.     call    SetPeriod
  1276.     test    ax,ax
  1277.     jnz    @@done
  1278.  
  1279. @@perdone:
  1280.     test    [di+s3mChannel.flags],128    ; is there a command?
  1281.     jz    @@resvol
  1282.     mov    bl,[di+s3mChannel.cmd]
  1283.     cmp    bl,"I"-"@"                      ; is command tremor
  1284.     je    @@notedone
  1285.     cmp    bl,"R"-"@"                      ; or tremolo?
  1286.     je    @@notedone
  1287.  
  1288. @@resvol:
  1289.     ; command not tremor or tremolo - reset volume:
  1290.     call    SetVolume
  1291.  
  1292. @@notedone:
  1293.     test    [di+s3mChannel.flags],64    ; if flag bit 6 is 1, there is
  1294.     jz    @@nosetvolume            ; a volume byte:
  1295.  
  1296.     mov    al,[di+s3mChannel.vol]        ; al = new volume
  1297.     cmp    al,64
  1298.     jb    @@volok             ; make sure volume is < 64
  1299.     mov    al,63
  1300. @@volok:
  1301.     mov    [di+s3mChannel.volume],al    ; set volume
  1302.     call    SetVolume
  1303.     test    ax,ax
  1304.     jnz    @@done
  1305.  
  1306.  
  1307. @@nosetvolume:
  1308.     test    [di+s3mChannel.flags],128    ; if flag bit 7 is 1, there
  1309.     jz    @@nocommand            ; is a command
  1310.  
  1311.     mov    al,[di+s3mChannel.info]     ; al = command infobyte
  1312.     test    al,al                ; if infobyte not zero, store
  1313.     jz    @@noinfo            ; it as the previous infobyte
  1314.     mov    [di+s3mChannel.preinfo],al
  1315. @@noinfo:
  1316.     movzx    ax,[di+s3mChannel.preinfo]    ; ax = command infobyte
  1317.  
  1318.     movzx    bx,[di+s3mChannel.cmd]        ; bx = command number
  1319.     cmp    bx,"Z"-"@"                      ; make sure command is valid
  1320.     ja    @@nocommand
  1321.     add    bx,bx
  1322.     call    [commands+bx]            ; process command
  1323.     test    ax,ax
  1324.     jnz    @@done
  1325.  
  1326.  
  1327. @@nocommand:
  1328.     add    di,size s3mChannel        ; next channel
  1329.     inc    [chan]
  1330.     inc    [sdChan]
  1331.  
  1332.     mov    ax,[chan]
  1333.     cmp    ax,[numChans]
  1334.     jb    @@chanloop
  1335.  
  1336.     cmp    [pbFlag],0
  1337.     jne    @@break
  1338.     inc    [row]            ; next row
  1339.     cmp    [row],64        ; did we reach pattern end?
  1340.     jb    @@noend
  1341.  
  1342.     mov    [row],0
  1343.  
  1344.     ; pattern end - move to next position:
  1345. @@break:
  1346.     call    NextPosition
  1347.     test    ax,ax
  1348.     jnz    @@done
  1349.  
  1350. @@noend:
  1351.     mov    [pbFlag],0        ; clear pattern break flag
  1352.     call    s3mUpdBars        ; update volume bars
  1353.  
  1354. @@done:
  1355. IFDEF DEBUGGER
  1356.     test    ax,ax
  1357.     jz    @@retu
  1358.  
  1359.     int 3
  1360.  
  1361. @@retu:
  1362. ENDIF
  1363.     ret
  1364. ENDP
  1365.  
  1366.  
  1367.  
  1368.  
  1369. ;/***************************************************************************\
  1370. ;*
  1371. ;* Function:    NextPosition
  1372. ;*
  1373. ;* Description: Move to next song position
  1374. ;*
  1375. ;* Returns:    MIDAS error code in ax
  1376. ;*
  1377. ;\***************************************************************************/
  1378.  
  1379. PROC    NextPosition    NEAR
  1380. USES    es, gs, si, di
  1381.  
  1382.     les    si,[module]        ; point es:si to module structure
  1383.  
  1384.     mov    bx,[position]
  1385.  
  1386. @@nextp:
  1387.     inc    bx            ; go to next position
  1388.  
  1389.     cmp    bx,[songLength]     ; did we reach song end?
  1390.     jae    @@restart        ; if so, restart
  1391.  
  1392.     cmp    bx,[loopEnd]        ; did we reach song loop end?
  1393.     jae    @@restart        ; if so, restart
  1394.  
  1395.     lgs    di,[es:si+mpModule.orders]    ; point gs:di to orders
  1396.     cmp    [byte gs:di+bx],0FEh        ; pattern number 0FEh?
  1397.     je    @@nextp             ; if yes, go to next pos
  1398.     cmp    [byte gs:di+bx],0FFh        ; pattern number 0FFh?
  1399.     jne    @@norestart            ; if not, do not restart
  1400.  
  1401. @@restart:
  1402.     inc    [loopCnt]        ; increase song loop counter
  1403.     mov    bx,[loopStart]        ; restart song
  1404.  
  1405. @@norestart:
  1406.     mov    [playOffset],2        ; skip pattern length word
  1407.     mov    [position],bx        ; store new position
  1408.     xor    ax,ax
  1409.     ret
  1410. ENDP
  1411.  
  1412.  
  1413.  
  1414.  
  1415. ;/***************************************************************************\
  1416. ;*
  1417. ;* Function:    SetVolume
  1418. ;*
  1419. ;* Description: Sets the volume on current channel to Sound Device, scaled
  1420. ;*        according to master volume
  1421. ;*
  1422. ;* Returns:    MIDAS error code in ax
  1423. ;*
  1424. ;\***************************************************************************/
  1425.  
  1426. PROC    SetVolume    NEAR
  1427.  
  1428.     mov    al,[di+s3mChannel.volume]
  1429.     call    SetSDVolume
  1430.     ret
  1431. ENDP
  1432.  
  1433.  
  1434.  
  1435.  
  1436. ;/***************************************************************************\
  1437. ;*
  1438. ;* Function:    SetSDVolume
  1439. ;*
  1440. ;* Description: Sets volume to Sound Device, scaled according to master volume
  1441. ;*
  1442. ;* Input:    al        volume
  1443. ;*
  1444. ;* Returns:    MIDAS error code in ax
  1445. ;*
  1446. ;\***************************************************************************/
  1447.  
  1448. PROC    SetSDVolume    NEAR
  1449.  
  1450.     or    [di+s3mChannel.status],1    ; volume changed
  1451.     mov    bl,[masterVolume]
  1452.     mul    bl                ; ax = actual volume
  1453.     shr    ax,6
  1454.  
  1455.     ; set volume to Sound Device:
  1456.     push    gs
  1457.     call    [gs:si+SoundDevice.SetVolume] LANG, [sdChan], ax
  1458.     pop    gs
  1459.  
  1460.     ret
  1461. ENDP
  1462.  
  1463.  
  1464.  
  1465.  
  1466. ;/***************************************************************************\
  1467. ;*
  1468. ;* Function:    SetSDPeriod
  1469. ;*
  1470. ;* Description: Sets period to Sound Device
  1471. ;*
  1472. ;* Input:    ax        period value
  1473. ;*
  1474. ;* Returns:    MIDAS error code in ax
  1475. ;*
  1476. ;\***************************************************************************/
  1477.  
  1478. PROC    SetSDPeriod    NEAR
  1479.  
  1480.     test    ax,ax            ; skip if period is zero
  1481.     jz    @@ok
  1482.  
  1483.     movzx    ecx,ax
  1484.     mov    eax,14317056        ; eax = sampling rate corresponding
  1485.     cdq                ; to the period value
  1486.     idiv    ecx
  1487.  
  1488.     push    gs
  1489.     call    [gs:si+SoundDevice.SetRate] LANG, [sdChan], eax
  1490.     pop    gs
  1491.     jmp    @@done
  1492.  
  1493. @@ok:
  1494.     xor    ax,ax
  1495.  
  1496. @@done:
  1497.     ret
  1498. ENDP
  1499.  
  1500.  
  1501.  
  1502.  
  1503. ;/***************************************************************************\
  1504. ;*
  1505. ;* Function:    SetPeriod
  1506. ;*
  1507. ;* Description: Sets the period on current channel to Sound Device and
  1508. ;*        limits it to period limits.
  1509. ;*
  1510. ;* Returns:    MIDAS error code in ax
  1511. ;*
  1512. ;\***************************************************************************/
  1513.  
  1514. PROC    SetPeriod    NEAR
  1515.  
  1516.     ; Adapted from STMIK 0.9beta with all the bugs that make S3M slides
  1517.     ; sound so interesting... ;)
  1518.  
  1519.     mov    ax,[di+s3mChannel.period]    ; ax = channel period
  1520.  
  1521.     mov    dx,[flags]        ; are Amiga-limits used?
  1522.     test    dx,10h
  1523.     jz    @@noamiga
  1524.  
  1525.     cmp    ax,[upperLimit]
  1526.     jbe    @@abelow        ; make sure period is below upper
  1527.     mov    ax,[upperLimit]     ; limit
  1528.     mov    [di+s3mChannel.period],ax
  1529. @@abelow:
  1530.     cmp    ax,[lowerLimit]
  1531.     jae    @@noamiga        ; make sure period is above lower
  1532.     mov    ax,[lowerLimit]     ; limit
  1533.     mov    [di+s3mChannel.period],ax
  1534.  
  1535. @@noamiga:
  1536.     ; no amiga limits - check with S3M limits
  1537.  
  1538.     cmp    ax,[upperLimit]
  1539.     jbe    @@below         ; make sure period is below upper
  1540.     mov    ax,[upperLimit]     ; limit
  1541.     test    dx,10h            ; S3M bug! If Amiga-limits are not
  1542.     jz    @@below         ; used, new period is not set to
  1543.     mov    [di+s3mChannel.period],ax    ; channel structure
  1544. @@below:
  1545.     cmp    ax,[lowerLimit]
  1546.     jae    @@above         ; make sure period is above lower
  1547.     mov    ax,[lowerLimit]     ; limit
  1548.     test    dx,10h            ; S3M bug! If Amiga-limits are not
  1549.     jz    @@above         ; used, new period is not set to
  1550.     mov    [di+s3mChannel.period],ax    ; channel structure
  1551. @@above:
  1552.  
  1553.     call    SetSDPeriod        ; set period to Sound Device
  1554.  
  1555. @@oiud: ret
  1556. ENDP
  1557.  
  1558.  
  1559.  
  1560.  
  1561. ;/***************************************************************************\
  1562. ;*
  1563. ;* Function:    SkipRows
  1564. ;*
  1565. ;* Description: Skips rows of song data. Used by Pattern Break and Pattern
  1566. ;*        Loop commands
  1567. ;*
  1568. ;* Input:    cx        number of rows to skip
  1569. ;*
  1570. ;* Returns:    MIDAS error code in ax
  1571. ;*
  1572. ;\***************************************************************************/
  1573.  
  1574. PROC    SkipRows    NEAR
  1575. LOCAL    rowCount : word, pattPtr : dword
  1576. USES    es,gs,si,di
  1577.  
  1578.     mov    cx,[row]
  1579.     test    cx,cx
  1580.     je    @@done
  1581.     mov    [rowCount],cx        ; store row counter
  1582.  
  1583.     les    si,[module]        ; point es:si to module structure
  1584.  
  1585.     mov    bx,[position]
  1586.     lgs    di,[es:si+mpModule.orders]    ; point gs:di to orders
  1587.     movzx    cx,[gs:di+bx]            ; cx = current pattern number
  1588.     shl    cx,2
  1589.  
  1590.     lgs    di,[es:si+mpModule.patterns]    ; point gs:di to current
  1591.     add    di,cx                ; pattern pointer
  1592.  
  1593.     push    gs di
  1594.     lgs    di,[es:si+mpModule.pattEMS]    ; point gs:di pattern EMS
  1595.     shr    cx,2                ; flags
  1596.     mov    bx,cx
  1597.     cmp    [byte gs:di+bx],0        ; is current pattern in EMS?
  1598.     pop    di gs
  1599.     je    @@noEMS
  1600.  
  1601.     ; map pattern data to conventional memory:
  1602.     call    emsMap LANG, [dword gs:di], seg s3mMemPtr offset s3mMemPtr
  1603.     test    ax,ax
  1604.     jnz    @@done
  1605.  
  1606.     mov    eax,[s3mMemPtr]     ; store pattern data pointer
  1607.     mov    [pattPtr],eax
  1608.     jmp    @@dataok
  1609.  
  1610. @@noEMS:
  1611.     mov    eax,[gs:di]        ; store pattern data pointer
  1612.     mov    [pattPtr],eax
  1613.  
  1614. @@dataok:
  1615.     les    si,[pattPtr]        ; point es:si to pattern data
  1616.     add    si,[playOffset]     ; point es:si to current row data
  1617.  
  1618.  
  1619. @@dataloop:
  1620.     mov    al,[es:si]        ; al = current channel flag byte
  1621.     inc    si
  1622.     test    al,al            ; is flag byte zero?
  1623.     jz    @@rowend        ; if is, end of row
  1624.  
  1625.     test    al,32            ; if bit 5 of flag is 1, there is a
  1626.     jz    @@nonote        ; note or instrument
  1627.     add    si,2            ; skip note and instrument bytes
  1628.  
  1629. @@nonote:
  1630.     test    al,64            ; if bit 6 of flag is 1, there is a
  1631.     jz    @@novol         ; volume change byte
  1632.     inc    si            ; skip volume change byte
  1633.  
  1634. @@novol:
  1635.     test    al,128            ; if bit 7 of flag 1 is, there is a
  1636.     jz    @@nocmd         ; command
  1637.     add    si,2            ; skip command and infobyte
  1638.  
  1639. @@nocmd:
  1640.     jmp    @@dataloop        ; get next flag byte and datas
  1641.  
  1642.  
  1643. @@rowend:
  1644.     dec    [rowCount]
  1645.     jnz    @@dataloop
  1646.  
  1647.     sub    si,[word pattPtr]    ; play offset = si - pattern start
  1648.     mov    [playOffset],si     ; offset
  1649.  
  1650. @@done:
  1651.     xor    ax,ax
  1652.     ret
  1653. ENDP
  1654.  
  1655.  
  1656.  
  1657.  
  1658.  
  1659. ;/***************************************************************************\
  1660. ;*     Scream Tracker 3 command processing:
  1661. ;\***************************************************************************/
  1662.  
  1663.  
  1664. ; Command A - Set Speed
  1665.  
  1666. PROC    SetSpeed    NEAR
  1667.  
  1668.     mov    al,[di+s3mChannel.info]     ; get current infobyte
  1669.     test    al,al                ; is it zero?
  1670.     jz    @@ok
  1671.     mov    [speed],al            ; no, set speed to infobyte
  1672. @@ok:
  1673.     xor    ax,ax
  1674.     ret
  1675. ENDP
  1676.  
  1677.  
  1678.  
  1679.  
  1680. ; Command B - Position Jump
  1681.  
  1682. PROC    PositionJump    NEAR
  1683.  
  1684.     movzx    ax,[di+s3mChannel.info] ; ax = infobyte
  1685.     cmp    [position],ax        ; jump backward?
  1686.     jb    @@noend
  1687.     inc    [loopCnt]        ; yes, increase song loop counter
  1688.  
  1689. @@noend:
  1690.     dec    ax            ; next position will be correct
  1691.     mov    [position],ax        ; set new position
  1692.     mov    [row],0         ; start from row 0
  1693.     mov    [pbFlag],1        ; break to new pattern
  1694.     xor    ax,ax
  1695.     ret
  1696. ENDP
  1697.  
  1698.  
  1699.  
  1700. ; Command C - Pattern Break
  1701.  
  1702. PROC    PatternBreak    NEAR
  1703.  
  1704.     mov    [pbFlag],1        ; set pattern break flag
  1705.     mov    [skipFlag],1        ; skip rows next time
  1706.  
  1707.     mov    al,[di+s3mChannel.info] ; al = infobyte
  1708.     mov    ah,al
  1709.     and    al,0Fh
  1710.     shr    ah,4            ; ax = new row number (infobyte is
  1711.     aad                ; in BCD)
  1712.     cmp    ax,63
  1713.     jbe    @@ok
  1714.     mov    ax,63
  1715. @@ok:    mov    [row],ax        ; set new row number
  1716.     xor    ax,ax
  1717.     ret
  1718. ENDP
  1719.  
  1720.  
  1721.  
  1722.  
  1723. ; Command D - Volume Slide
  1724.  
  1725. PROC    VolumeSlide    NEAR
  1726.  
  1727.     mov    bl,al            ; bl = upper nybble - amount to add
  1728.     shr    bl,4            ; to volume
  1729.     mov    cl,al            ; cl = lower nybble - amount to
  1730.     and    cl,0Fh            ; substract from volume
  1731.  
  1732.     cmp    cl,0Fh            ; is lower nybble 0Fh?
  1733.     je    @@addfine        ; if is, fine volume slide up
  1734.     cmp    bl,0Fh            ; is upper nybble 0Fh?
  1735.     je    @@subfine        ; if is, fine volume slide down
  1736.  
  1737.     test    cl,cl            ; is lower nybble zero?
  1738.     jz    @@add            ; is is, slide up
  1739.     jmp    @@sub
  1740.  
  1741. @@subfine:
  1742.     ; fine volume slide down
  1743.     cmp    [playCount],0        ; do only if player counter is 0
  1744.     jne    @@ok
  1745.  
  1746. @@sub:    sub    [di+s3mChannel.volume],cl    ; volume slide down -
  1747.     jns    @@setv                ; substract infobyte lower
  1748.     mov    [di+s3mChannel.volume],0    ; nybble from volume. Check
  1749.     jmp    @@setv                ; that volume is not negative
  1750.  
  1751. @@addfine:
  1752.     ; fine volume slide down
  1753.     test    bl,bl            ; S3M "bug" - if upper nybble is zero,
  1754.     jz    @@sub            ; normal slide down with speed 0Fh.
  1755.  
  1756.     cmp    [playCount],0        ; fine slide - do only if player
  1757.     jne    @@ok            ; counter is 0
  1758.  
  1759. @@add:    add    [di+s3mChannel.volume],bl    ; volume slide up - add
  1760.     cmp    [di+s3mChannel.volume],64    ; infobyte upper nybble to
  1761.     jb    @@setv                ; volume. Check that volume
  1762.     mov    [di+s3mChannel.volume],63    ; is < 64
  1763.  
  1764. @@setv:
  1765.     call    SetVolume        ; set volume to Sound Device
  1766.     jmp    @@done
  1767.  
  1768. @@ok:
  1769.     xor    ax,ax
  1770.  
  1771. @@done:
  1772.     ret
  1773. ENDP
  1774.  
  1775.  
  1776.  
  1777.  
  1778. ; Command E - Slide Down
  1779.  
  1780. PROC    SlideDown NEAR
  1781.  
  1782.     cmp    [playCount],0        ; is player counter 0?
  1783.     je    @@dofine        ; if is, do only fine slides
  1784.  
  1785.     cmp    ax,0E0h         ; player counter is != 0 - do not
  1786.     jae    @@ok            ; do fine slides
  1787.     shl    ax,2            ; ax = period slide speed
  1788.     jmp    @@doslide
  1789.  
  1790.  
  1791. @@dofine:
  1792.     cmp    ax,0E0h         ; player counter is 0 - do only
  1793.     jbe    @@ok            ; fine slides
  1794.     cmp    ax,0F0h         ; extra fine slide?
  1795.     jbe    @@efine
  1796.  
  1797.     and    ax,0Fh            ; fine slide - use lower nybble * 4
  1798.     shl    ax,2            ; as period slide speed
  1799.     jmp    @@doslide
  1800.  
  1801. @@efine:
  1802.     and    ax,0Fh            ; extra fine slide - use lower nybble
  1803.                     ; as period slide speed
  1804.  
  1805. @@doslide:
  1806.     add    [di+s3mChannel.period],ax      ; slide down - add ax to period
  1807.     call    SetPeriod            ; set period to Sound Device
  1808.     jmp    @@done
  1809.  
  1810. @@ok:
  1811.     xor    ax,ax
  1812.  
  1813. @@done:
  1814.     ret
  1815. ENDP
  1816.  
  1817.  
  1818.  
  1819.  
  1820. ; Command F - Slide Up
  1821.  
  1822. PROC    SlideUp     NEAR
  1823.  
  1824.     cmp    [playCount],0        ; is player counter 0?
  1825.     je    @@dofine        ; if is, do only fine slides
  1826.  
  1827.     cmp    ax,0E0h         ; player counter is != 0 - do not
  1828.     jae    @@ok            ; do fine slides
  1829.     shl    ax,2            ; ax = period slide speed
  1830.     jmp    @@doslide
  1831.  
  1832.  
  1833. @@dofine:
  1834.     cmp    ax,0E0h         ; player counter is 0 - do only
  1835.     jbe    @@ok            ; fine slides
  1836.     cmp    ax,0F0h         ; extra fine slide?
  1837.     jbe    @@efine
  1838.  
  1839.     and    ax,0Fh            ; fine slide - use lower nybble * 4
  1840.     shl    ax,2            ; as period slide speed
  1841.     jmp    @@doslide
  1842.  
  1843. @@efine:
  1844.     and    ax,0Fh            ; extra fine slide - use lower nybble
  1845.                     ; as period slide speed
  1846.  
  1847. @@doslide:
  1848.     sub    [di+s3mChannel.period],ax    ; slide down - substract ax
  1849.                         ; from period
  1850.     call    SetPeriod            ; set period to Sound Device
  1851.     jmp    @@done
  1852.  
  1853. @@ok:
  1854.     xor    ax,ax
  1855.  
  1856. @@done:
  1857.     ret
  1858. ENDP
  1859.  
  1860.  
  1861.  
  1862.  
  1863. ; Command G - Tone Portamento
  1864.  
  1865. PROC NOLANGUAGE TonePortamento    NEAR
  1866.  
  1867.     movzx    ax,[di+s3mChannel.info]     ; ax = infobyte
  1868.     test    ax,ax                ; is infobyte zero?
  1869.     jnz    @@old
  1870.  
  1871. ContinueTonePortamento:
  1872.     movzx    ax,[di+s3mChannel.notepsp]    ; if is, use old porta speed
  1873.  
  1874. @@old:    mov    [di+s3mChannel.notepsp],al    ; store portamento speed
  1875.     mov    bx,[di+s3mChannel.toperi]    ; bx = slide destination
  1876.     test    bx,bx                ; is destination zero?
  1877.     jz    @@setperiod            ; if is, skip
  1878.  
  1879.     shl    ax,2            ; period slide speed = speed * 4
  1880.  
  1881.     cmp    [di+s3mChannel.period],bx    ; should we slide up?
  1882.     jg    @@up
  1883.  
  1884.     ; slide down:
  1885.     add    [di+s3mChannel.period],ax    ; increase period
  1886.     cmp    [di+s3mChannel.period],bx    ; past portamento dest?
  1887.     jl    @@setperiod
  1888.     mov    [di+s3mChannel.period],bx    ; if yes, set to porta dest
  1889.     mov    [di+s3mChannel.toperi],0    ; do not slide anymore
  1890.     jmp    @@setperiod
  1891.  
  1892. @@up:
  1893.     ; slide up:
  1894.     sub    [di+s3mChannel.period],ax    ; decrease period
  1895.     cmp    [di+s3mChannel.period],bx    ; past portamento dest?
  1896.     jg    @@setperiod
  1897.     mov    [di+s3mChannel.period],bx    ; if yes, set to porta dest
  1898.     mov    [di+s3mChannel.toperi],0    ; do not slide anymore
  1899.  
  1900. @@setperiod:
  1901.     call    SetPeriod
  1902.     ret
  1903. ENDP
  1904.  
  1905.  
  1906.  
  1907.  
  1908. ; Command H - Vibrato
  1909.  
  1910. PROC NOLANGUAGE Vibrato     NEAR
  1911.  
  1912.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  1913.     test    al,al
  1914.     jnz    @@nozero
  1915.     mov    al,[di+s3mChannel.vibcmd]
  1916.  
  1917. @@nozero:
  1918.     test    al,0F0h         ; is infobyte upper nybble zero?
  1919.     jnz    @@1
  1920.  
  1921.     mov    ah,[di+s3mChannel.vibcmd]    ; yes, use old vibrato
  1922.                         ; infobyte upper nybble
  1923.     and    al,0fh
  1924.     and    ah,0f0h
  1925.     or    al,ah
  1926. @@1:
  1927.     mov    [di+s3mChannel.vibcmd],al    ; store vibrato infobyte
  1928.  
  1929. ContinueVibrato:
  1930.     mov    bl,[di+s3mChannel.vibpos]
  1931.     and    bx,1Fh                ; bx = vibrato position
  1932.     mov    al,[vibratoTable+bx]        ; al = vibrato value
  1933.     mov    cl,[di+s3mChannel.vibcmd]    ; multiply with depth
  1934.     and    cl,0Fh
  1935.     mul    cl
  1936.  
  1937.     shr    ax,4                ; divide with 16 - ST2 vibrato
  1938.  
  1939.     test    [flags],1            ; is Scream Tracker 2 vibrato
  1940.     jnz    @@st2vib            ; used?
  1941.     shr    ax,1                ; no, divide still with 2
  1942.  
  1943. @@st2vib:
  1944.     movzx    eax,ax                ; eax = vibrato value
  1945.  
  1946.     movzx    ebx,[di+s3mChannel.period]    ; ebx = vibrato base period
  1947.  
  1948.     test    [di+s3mChannel.vibpos],32    ; is vibrato position >= 32?
  1949.     jnz    @@vibneg
  1950.  
  1951.     add    ebx,eax             ; vibrato position < 32 -
  1952.     jmp    @@setper            ; positiove
  1953.  
  1954. @@vibneg:
  1955.     sub    ebx,eax             ; vibrato position >= 32 -
  1956.                         ; negative
  1957.  
  1958. @@setper:
  1959.     mov    al,[di+s3mChannel.vibcmd]
  1960.     shr    al,4                ; update vibrato position
  1961.     add    [di+s3mChannel.vibpos],al
  1962.  
  1963.     mov    ax,bx                ; set new period to
  1964.     call    SetSDPeriod            ; Sound Device
  1965.     ret
  1966. ENDP
  1967.  
  1968.  
  1969. ; Command I - Tremor
  1970.  
  1971. PROC NOLANGUAGE Tremor    NEAR
  1972.  
  1973.     mov    bl,[di+s3mChannel.trecnt]    ; bl = tremor count
  1974.     test    bl,bl                ; is tremor counter zero?
  1975.     jz    @@change
  1976.  
  1977.     dec    bl                ; no, decrement it
  1978.     mov    [di+s3mChannel.trecnt],bl
  1979.     jmp    @@ok                ; and skip the rest
  1980.  
  1981. @@change:
  1982.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  1983.     cmp    [di+s3mChannel.trefl],1     ; is current tremor flag 1?
  1984.     je    @@off                ; if is, turn sound off
  1985.  
  1986.     ; current tremor flag is 0 - turn sound on for infobyte upper nybble
  1987.     ; frames:
  1988.  
  1989.     shr    al,4
  1990.     mov    [di+s3mChannel.trecnt],al    ; tremor count = upper nybble
  1991.     mov    [di+s3mChannel.trefl],1     ; set flag to 1
  1992.     call    SetVolume            ; set normal channel volume
  1993.     jmp    @@done
  1994.  
  1995. @@off:    ; current tremor flag is 1 - turn sound on for infobyte lower nybble
  1996.     ; frames:
  1997.  
  1998.     and    al,0Fh
  1999.     mov    [di+s3mChannel.trecnt],al    ; tremor count = lower nybble
  2000.     mov    [di+s3mChannel.trefl],0     ; set flag to 0
  2001.     xor    al,al
  2002.     call    SetSDVolume            ; Set Sound Device volume to 0
  2003.     jmp    @@done
  2004. @@ok:
  2005.     xor    ax,ax
  2006. @@done:
  2007.     ret
  2008. ENDP
  2009.  
  2010.  
  2011.  
  2012. ; Command J - Arpeggio
  2013.  
  2014. PROC NOLANGUAGE Arpeggio    NEAR
  2015.  
  2016.     mov    bl,[di+s3mChannel.snote]    ; bl = current note
  2017.  
  2018.     mov    cl,bl
  2019.     and    bx,0Fh                ; bx = note number,
  2020.     shr    cl,4                ; cl = octave
  2021.  
  2022.     push    cx
  2023.     movzx    ax,[playCount]
  2024.     mov    cl,3            ; divide player counter with 3
  2025.     div    cl            ; - ah = modulus
  2026.     pop    cx
  2027.  
  2028.     test    ah,ah            ; is modulus zero?
  2029.     jz    @@a0            ; if yes, play with base note
  2030.     dec    ah            ; is it 1?
  2031.     jz    @@a1
  2032.  
  2033.     ; modulus is 2 - add infobyte lower nybble to note
  2034.     mov    al,[di+s3mChannel.preinfo]
  2035.     and    ax,0Fh
  2036.     add    bx,ax
  2037.     jmp    @@a0
  2038.  
  2039. @@a1:
  2040.     ; modulus is 1 - add infobyte upper nybble to note
  2041.     movzx    ax,[di+s3mChannel.preinfo]
  2042.     shr    ax,4
  2043.     add    bx,ax
  2044.  
  2045. @@a0:
  2046.     cmp    bx,12            ; check if we crossed an octave
  2047.     jb    @@nooct         ; boundary
  2048.     sub    bx,12            ; yes, substract 12 from note and
  2049.     inc    cl            ; increment octave
  2050. @@nooct:
  2051.  
  2052.     ; Calculate sampling rate that corresponds to new note. A crazy
  2053.     ; method, but this is what the Scream Tracker 3 documentation
  2054.     ; says:
  2055.  
  2056. ;              8363 * 16 * ( period(NOTE) >> octave(NOTE) )
  2057. ;     note_st3period = --------------------------------------------
  2058. ;              middle_c_finetunevalue(INSTRUMENT)
  2059. ;
  2060. ;     note_amigaperiod = note_st3period / 4
  2061. ;
  2062. ;     note_herz=14317056 / note_st3period
  2063.  
  2064.  
  2065.     add    bx,bx
  2066.     movzx    eax,[Periods+bx]        ; eax = new note period
  2067.     imul    eax,eax,8363*16         ; eax = 8363 * 16 * period
  2068.     shr    eax,cl                ;    / 2^oct
  2069.  
  2070.     movzx    bx,[di+s3mChannel.sample]
  2071.  
  2072.     push    si
  2073.     les    si,[module]
  2074.     les    si,[es:si+mpModule.insts]
  2075.     dec    bx                ; point es:si to current
  2076.     imul    bx,bx,SIZE mpInstrument     ; instrument structure
  2077.     add    si,bx
  2078.  
  2079.     mov    ebx,[es:si+mpInstrument.c2Rate] ; ebx = instrument C4 sampling
  2080.     pop    si                ; rate
  2081.  
  2082.     cdq                    ; eax = period = 8366 * 16
  2083.     idiv    ebx                ;   * period / 2^oct / c2Rate
  2084.  
  2085.     call    SetSDPeriod        ; set new period to Sound Device
  2086.     ret
  2087. ENDP
  2088.  
  2089.  
  2090.  
  2091.  
  2092. ; Command K - Vibrato and Volume Slide
  2093.  
  2094. PROC NOLANGUAGE VibratoVolumeSlide    NEAR
  2095.  
  2096.     call    VolumeSlide
  2097.     test    ax,ax
  2098.     jnz    @@done
  2099.  
  2100.     call    ContinueVibrato
  2101. @@done:
  2102.     ret
  2103. ENDP
  2104.  
  2105.  
  2106.  
  2107. ; Command L - Tone Portamento and Volume Slide
  2108.  
  2109. PROC NOLANGUAGE TonePortamentoVolumeSlide NEAR
  2110.  
  2111.     call    VolumeSlide
  2112.     test    ax,ax
  2113.     jnz    @@done
  2114.  
  2115.     call    ContinueTonePortamento
  2116. @@done:
  2117.     ret
  2118. ENDP
  2119.  
  2120.  
  2121.  
  2122.  
  2123. ; Command Q - Retrig Note
  2124.  
  2125. PROC NOLANGUAGE RetrigNote    NEAR
  2126.  
  2127.     mov    al,[di+s3mChannel.retrigc]    ; get retrig count
  2128.     dec    al                ; decrement it
  2129.     cmp    al,0                ; if counter is <= 0, retrig
  2130.     jle    @@retrig
  2131.  
  2132.     mov    [di+s3mChannel.retrigc],al    ; store new counter
  2133.     jmp    @@ok
  2134.  
  2135. @@retrig:
  2136.     push    gs
  2137.     call    [gs:si+SoundDevice.SetPosition], [sdChan], 0
  2138.     pop    gs
  2139.     test    ax,ax
  2140.     jnz    @@done
  2141.  
  2142.     mov    al,[di+s3mChannel.preinfo]
  2143.     mov    bl,al                ; set infobyte lower nybble
  2144.     and    al,0Fh                ; as retrig count
  2145.     mov    [di+s3mChannel.retrigc],al
  2146.  
  2147.     ; Retrig note volume slides: (this apprarently, works, though I
  2148.     ; do not why)
  2149.  
  2150.     shr    bl,4                ; bx = infobyte upper nybble
  2151.     xor    bh,bh                ; = index to retrig table
  2152.     mov    al,[retrigTable2+bx]        ; get value from table
  2153.     test    al,al                ; was it zero?
  2154.     jz    @@1
  2155.  
  2156.     mov    cl,[di+s3mChannel.volume]    ; not zero, multiply volume
  2157.     mul    cl                ; with table value
  2158.     shr    ax,4                ; and divide with 16
  2159.     mov    [di+s3mChannel.volume],al    ; set new volume
  2160.     jmp    @@check
  2161.  
  2162. @@1:    mov    al,[retrigTable1+bx]          ; value from table zero - get
  2163.     add    [di+s3mChannel.volume],al     ; volume increment from table 1
  2164.  
  2165. @@check:
  2166.     mov    al,[di+s3mChannel.volume]
  2167.     cmp    al,0                ; make sure that volume is
  2168.     jge    @@notbelow            ; not below zero
  2169.     mov    [di+s3mChannel.volume],0
  2170. @@notbelow:
  2171.     cmp    al,64
  2172.     jl    @@eiyli64            ; make sure that volume is
  2173.     mov    [di+s3mChannel.volume],63    ; not above 63
  2174. @@eiyli64:
  2175.     or    [di+s3mChannel.status],3    ; force volume value
  2176.     call    SetVolume            ; set volume to Sound Device
  2177.     jmp    @@done
  2178.  
  2179. @@ok:
  2180.     xor    ax,ax
  2181. @@done:
  2182.     ret
  2183. ENDP
  2184.  
  2185.  
  2186.  
  2187.  
  2188. ; Command R - Tremolo
  2189.  
  2190. PROC NOLANGUAGE Tremolo     NEAR
  2191.  
  2192.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  2193.     test    al,al
  2194.     jnz    @@nozero
  2195.     mov    al,[di+s3mChannel.vibcmd]
  2196.  
  2197. @@nozero:
  2198.     test    al,0F0h         ; is infobyte upper nybble zero?
  2199.     jnz    @@1
  2200.  
  2201.     mov    ah,[di+s3mChannel.vibcmd]    ; yes, use old vibrato
  2202.                         ; infobyte upper nybble
  2203.     and    al,0fh
  2204.     and    ah,0f0h
  2205.     or    al,ah
  2206. @@1:
  2207.     mov    [di+s3mChannel.vibcmd],al    ; store vibrato infobyte
  2208.  
  2209.     mov    bl,[di+s3mChannel.vibpos]
  2210.     and    bx,1Fh                ; bx = vibrato position
  2211.     mov    al,[vibratoTable+bx]        ; al = vibrato value
  2212.     mov    cl,[di+s3mChannel.vibcmd]    ; multiply with depth
  2213.     and    cl,0Fh
  2214.     mul    cl
  2215.  
  2216.     shr    ax,7                ; divide with 128
  2217.  
  2218.     movzx    bx,[di+s3mChannel.volume]    ; bx = tremolo base volume
  2219.  
  2220.     test    [di+s3mChannel.vibpos],32     ; is vibrato positio >= 32?
  2221.     jnz    @@vibneg
  2222.  
  2223.     add    bx,ax                ; vibrato position < 32 -
  2224.     jmp    @@setvol            ; positiove
  2225.  
  2226. @@vibneg:
  2227.     sub    bx,ax                ; vibrato position >= 32 -
  2228.                         ; negative
  2229.  
  2230. @@setvol:
  2231.     mov    al,[di+s3mChannel.vibcmd]
  2232.     shr    al,4                ; update vibrato position
  2233.     add    [di+s3mChannel.vibpos],al
  2234.  
  2235.     or    [di+s3mChannel.status],1    ; volume changed
  2236.  
  2237.     cmp    bx,0
  2238.     jge    @@notbelow            ; make sure volume is not
  2239.     xor    bx,bx                ; negative
  2240. @@notbelow:
  2241.     cmp    bx,64
  2242.     jl    @@notabove            ; make sure volume is not
  2243.     mov    bx,63                ; above 64
  2244. @@notabove:
  2245.     mov    al,bl
  2246.     call    SetSDVolume            ; set new volume to
  2247.     ret                    ; Sound Device
  2248. ENDP
  2249.  
  2250.  
  2251.  
  2252.  
  2253. ; Command T - Set Tempo
  2254.  
  2255. PROC NOLANGUAGE SetTempo    NEAR
  2256.  
  2257.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  2258.     mov    [tempo],al            ; BPM tempo = infobyte
  2259.  
  2260.     xor    ah,ah
  2261.     mov    bx,40                ; BPM * 40 = update rate in
  2262.     mul    bx                ; 100*Hz
  2263.     mov    [mpS3M.updRate],ax
  2264.     mov    bx,ax
  2265.  
  2266.     ; Set Sound Device update rate
  2267.     push    gs bx
  2268.     call    [gs:si+SoundDevice.SetUpdRate] LANG, bx
  2269.     pop    bx gs
  2270.     test    ax,ax
  2271.     jnz    @@done
  2272.  
  2273.     ; Set Timer update rate:
  2274.     push    gs
  2275.     call    tmrSetUpdRate LANG, bx
  2276.     pop    gs
  2277.  
  2278. @@done:
  2279.     ret
  2280. ENDP
  2281.  
  2282.  
  2283.  
  2284.  
  2285. ; Command V - Set Master Volume
  2286.  
  2287. PROC NOLANGUAGE SetMasterVolume     NEAR
  2288.  
  2289.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  2290.  
  2291.     cmp    al,64
  2292.     jbe    @@1                ; make sure master volume
  2293.     mov    al,64                ; is not above 64
  2294.  
  2295. @@1:    mov    [masterVolume],al        ; store master volume
  2296.  
  2297.     xor    ax,ax
  2298.  
  2299.     ret
  2300. ENDP
  2301.  
  2302.  
  2303. ; Command X - Set Panning
  2304.  
  2305. PROC NOLANGUAGE SetPanning NEAR
  2306.  
  2307.     mov    al,[di+s3mChannel.info]     ; al = infobyte
  2308.     cmp    [usePanning],0        ; should panning command be supported?
  2309.     je    @@ok            ; skip if not
  2310.  
  2311.     cmp    al,0A4h         ; DMP-compatible surround panning
  2312.     jne    @@nsurround        ; value 0A4h
  2313.  
  2314.     mov    ax,panSurround        ; set surround panning
  2315.     jmp    @@set
  2316.  
  2317. @@nsurround:
  2318.     cmp    al,128            ; skip illegal panning values
  2319.     ja    @@ok
  2320.  
  2321.     sub    al,40h            ; convert DMP panning values to
  2322.     cbw                ; MIDAS (0-128) to (-64 - 64)
  2323.  
  2324. @@set:
  2325.     mov    bx,[chan]        ; bx = Sound Device channel number
  2326.     add    bx,[firstSDChan]
  2327.  
  2328.     ; Set Sound Device panning:
  2329.     push    gs
  2330.     call    [gs:si+SoundDevice.SetPanning], bx, ax
  2331.     pop    gs
  2332.     jmp    @@done
  2333.  
  2334. @@ok:
  2335.     xor    ax,ax
  2336.  
  2337. @@done:
  2338.     ret
  2339. ENDP
  2340.  
  2341.  
  2342.  
  2343.  
  2344.  
  2345.  
  2346. ; Command S - Special Commands
  2347.  
  2348. PROC NOLANGUAGE SpecialCommands     NEAR
  2349.  
  2350.     mov    bl,al            ; al = infobyte (can't be zero!)
  2351.     and    ax,0Fh            ; ax = actual S-command infobyte
  2352.  
  2353.     and    bx,0F0h         ; bx = index to S-command offset
  2354.     shr    bx,3            ; table
  2355.     call    [scommands+bx]        ; process command
  2356.     ret
  2357. ENDP
  2358.  
  2359.  
  2360. ; Command S8 - 16-Step Set Panning
  2361.  
  2362. PROC NOLANGUAGE SetPanning16 NEAR
  2363.  
  2364.     cmp    [usePanning],0        ; should panning command be supported?
  2365.     je    @@ok            ; skip if not
  2366.  
  2367.     sub    ax,8
  2368.     js    @@ski
  2369.     inc    ax
  2370. @@ski:
  2371.     sal    ax,3
  2372.  
  2373.     mov    bx,[chan]        ; bx = Sound Device channel number
  2374.     add    bx,[firstSDChan]
  2375.  
  2376.     ; Set Sound Device panning:
  2377.     push    gs
  2378.     call    [gs:si+SoundDevice.SetPanning], bx, ax
  2379.     pop    gs
  2380.     jmp    @@done
  2381. @@ok:
  2382.     xor    ax,ax
  2383. @@done:
  2384.     ret
  2385. ENDP
  2386.  
  2387.  
  2388.  
  2389. ; Command SC - Note Cut
  2390.  
  2391. PROC NOLANGUAGE NoteCut     NEAR
  2392.  
  2393.     cmp    [playCount],al        ; cut note when player count is equal
  2394.     jne    @@ok            ; to infobyte
  2395.  
  2396.     mov    [di+s3mChannel.volume],0    ; set volume to zero
  2397.     call    SetVolume        ; set volume to Sound Device
  2398.     jmp    @@done
  2399.  
  2400. @@ok:
  2401.     xor    ax,ax
  2402.  
  2403. @@done:
  2404.     ret
  2405. ENDP
  2406.  
  2407.  
  2408.  
  2409.  
  2410. ; Command SD - Note Delay
  2411.  
  2412. PROC NOLANGUAGE NoteDelay    NEAR
  2413.  
  2414.     cmp    [playCount],al        ; start note whey player counter is
  2415.     jne    @@ok            ; equal to infobyte
  2416.  
  2417.     mov    bl,[di+s3mChannel.note]     ; bl = note
  2418.     cmp    bl,255                ; skip if empty note
  2419.     je    @@nonewnote
  2420.     cmp    bl,254                ; is note key off?
  2421.     je    @@keyoff
  2422.  
  2423.     mov    [di+s3mChannel.snote],bl    ; set note
  2424.  
  2425.     mov    cl,bl                ; cl = new note octave
  2426.     shr    cl,4
  2427.  
  2428.     ; Calculate sampling rate that corresponds to new note. A crazy
  2429.     ; method, but this is what the Scream Tracker 3 documentation
  2430.     ; says:
  2431.  
  2432. ;              8363 * 16 * ( period(NOTE) >> octave(NOTE) )
  2433. ;     note_st3period = --------------------------------------------
  2434. ;              middle_c_finetunevalue(INSTRUMENT)
  2435. ;
  2436. ;     note_amigaperiod = note_st3period / 4
  2437. ;
  2438. ;     note_herz=14317056 / note_st3period
  2439.  
  2440.  
  2441.     and    bx,0Fh
  2442.     add    bx,bx
  2443.     movzx    eax,[Periods+bx]        ; eax = new note period
  2444.     imul    eax,eax,8363*16         ; eax = 8363 * 16 * period
  2445.     shr    eax,cl                ;    / 2^oct
  2446.  
  2447.     movzx    bx,[di+s3mChannel.sample]
  2448.  
  2449.     push    si
  2450.     les    si,[module]
  2451.     les    si,[es:si+mpModule.insts]
  2452.     dec    bx                ; point es:si to current
  2453.     imul    bx,bx,SIZE mpInstrument     ; instrument structure
  2454.     add    si,bx
  2455.  
  2456.     mov    ebx,[es:si+mpInstrument.c2Rate] ; ebx = instrument C4 sampling
  2457.     pop    si                ; rate
  2458.  
  2459.     test    ebx,ebx             ; is c2Rate zero?
  2460.     jz    @@1
  2461.     cdq                    ; eax = period = 8366 * 16
  2462.     idiv    ebx                ;   * period / 2^oct / c2Rate
  2463.  
  2464. @@1:    or    [di+s3mChannel.status],3    ; force volume
  2465.     mov    [di+s3mChannel.period],ax    ; store current period number
  2466.  
  2467.     mov    ebx,eax             ; ebx = period
  2468.     test    ebx,ebx             ; do not set note if period
  2469.     jz    @@nonewnote            ; is zero
  2470.  
  2471.     mov    eax,14317056
  2472.     cdq                ; ebx = note sampling rate in Hz
  2473.     idiv    ebx
  2474.  
  2475.     ; start playing current note with Sound Device:
  2476.     push    gs
  2477.     call    [gs:si+SoundDevice.PlaySound] LANG, [sdChan], eax
  2478.     pop    gs
  2479.     test    ax,ax
  2480.     jmp    @@done
  2481.  
  2482. @@nonewnote:
  2483.     jmp    @@ok
  2484.  
  2485. @@keyoff:
  2486.     ; note is key off - stop playing sound:
  2487.     push    gs
  2488.     call    [gs:si+SoundDevice.StopSound] LANG, [sdChan]
  2489.     pop    gs
  2490.     jmp    @@done
  2491.  
  2492.  
  2493. @@ok:
  2494.     xor    ax,ax
  2495.  
  2496. @@done:
  2497.     ret
  2498. ENDP
  2499.  
  2500.  
  2501.  
  2502.  
  2503. ; Command SB - Pattern Loop
  2504.  
  2505. PROC NOLANGUAGE PatternLoop    NEAR
  2506.  
  2507.     cmp    [playCount],0        ; do only when player count is 0
  2508.     jne    @@ok
  2509.  
  2510.     test    al,al            ; if infobyte is zero, set loop
  2511.     jz    @@setloop        ; start
  2512.  
  2513.     cmp    [loopFlag],0        ; already looping?
  2514.     je    @@setcount        ; if not, set loop count
  2515.  
  2516.     dec    [loopCount]        ; decrement loop counter
  2517.     jnz    @@loop            ; if not zero, jump to loop beginning
  2518.     mov    [loopFlag],0        ; zero - do not loop
  2519.     jmp    @@ok
  2520.  
  2521.  
  2522. @@loop: mov    ax,[loopRow]
  2523.     dec    ax            ; set loop start row
  2524.     mov    [row],ax
  2525.  
  2526.     mov    ax,[loopOffset]     ; and playing offset
  2527.     mov    [playOffset],ax
  2528.     jmp    @@ok
  2529.  
  2530. @@setcount:
  2531.     mov    [loopCount],al        ; set loop counter
  2532.     mov    [loopFlag],1        ; looping
  2533.     jmp    @@loop
  2534.  
  2535. @@setloop:
  2536.     mov    ax,[row]        ; save loop start row and playing
  2537.     mov    [loopRow],ax        ; offset
  2538.     mov    ax,[oldOffset]
  2539.     mov    [loopOffset],ax
  2540.  
  2541. @@ok:
  2542.     xor    ax,ax
  2543.  
  2544.     ret
  2545. ENDP
  2546.  
  2547.  
  2548.  
  2549.  
  2550. ; Command SD - Pattern Delay
  2551.  
  2552. PROC NOLANGUAGE PatternDelay  NEAR
  2553.  
  2554.     cmp    [delayFlag],0        ; pattern delay already active?
  2555.     jne    @@ok
  2556.  
  2557.     mov    [delayCount],al     ; set delay counter
  2558.     mov    [delayFlag],1
  2559.  
  2560. @@ok:
  2561.     xor    ax,ax
  2562.     ret
  2563. ENDP
  2564.  
  2565.  
  2566.  
  2567.  
  2568. ; Empty
  2569.  
  2570. PROC NOLANGUAGE DoNothing    NEAR
  2571.  
  2572.     xor    ax,ax
  2573.     ret
  2574. ENDP
  2575.  
  2576.  
  2577.  
  2578.  
  2579. ;/***************************************************************************\
  2580. ;*     Calling offset tables to commands:
  2581. ;\***************************************************************************/
  2582.  
  2583.     ; Commands run when song data is played:
  2584. LABEL    commands    WORD
  2585.     DW    offset DoNothing
  2586.     DW    offset SetSpeed
  2587.     DW    offset PositionJump
  2588.     DW    offset PatternBreak
  2589.     DW    offset VolumeSlide
  2590.     DW    offset SlideDown
  2591.     DW    offset SlideUp
  2592.     DW    offset DoNothing
  2593.     DW    offset DoNothing
  2594.     DW    offset Tremor
  2595.     DW    offset Arpeggio
  2596.     DW    offset DoNothing
  2597.     DW    offset DoNothing
  2598.     DW    offset DoNothing
  2599.     DW    offset DoNothing
  2600.     DW    offset DoNothing
  2601.     DW    offset DoNothing
  2602.     DW    offset RetrigNote
  2603.     DW    offset DoNothing
  2604.     DW    offset SpecialCommands
  2605.     DW    offset SetTempo
  2606.     DW    offset DoNothing
  2607.     DW    offset SetMasterVolume
  2608.     DW    offset DoNothing
  2609.     DW    offset SetPanning
  2610.     DW    offset DoNothing
  2611.     DW    offset DoNothing
  2612.  
  2613.     ; continuous commands:
  2614. LABEL    contCommands    WORD
  2615.     DW    offset DoNothing
  2616.     DW    offset DoNothing
  2617.     DW    offset DoNothing
  2618.     DW    offset DoNothing
  2619.     DW    offset VolumeSlide
  2620.     DW    offset SlideDown
  2621.     DW    offset SlideUp
  2622.     DW    offset TonePortamento
  2623.     DW    offset Vibrato
  2624.     DW    Offset Tremor
  2625.     DW    offset Arpeggio
  2626.     DW    offset VibratoVolumeSlide
  2627.     DW    offset TonePortamentoVolumeSlide
  2628.     DW    offset DoNothing
  2629.     DW    offset DoNothing
  2630.     DW    offset DoNothing
  2631.     DW    offset DoNothing
  2632.     DW    offset RetrigNote
  2633.     DW    offset Tremolo
  2634.     DW    offset SpecialCommands
  2635.     DW    offset DoNothing
  2636.     DW    offset DoNothing
  2637.     DW    offset DoNothing
  2638.     DW    offset DoNothing
  2639.     DW    offset DoNothing
  2640.     DW    offset DoNothing
  2641.     DW    offset DoNothing
  2642.  
  2643.  
  2644.     ; S-commands:
  2645. LABEL    scommands    WORD
  2646.     DW    offset DoNothing    ; 0
  2647.     DW    offset DoNothing    ; 1
  2648.     DW    offset DoNothing    ; 2
  2649.     DW    offset DoNothing    ; 3
  2650.     DW    offset DoNothing    ; 4
  2651.     DW    offset DoNothing    ; 5
  2652.     DW    offset DoNothing    ; 6
  2653.     DW    offset DoNothing    ; 7
  2654.     DW    offset SetPanning16    ; 8
  2655.     DW    offset DoNothing    ; 9
  2656.     DW    offset DoNothing    ; A
  2657.     DW    offset PatternLoop    ; B
  2658.     DW    offset NoteCut        ; C
  2659.     DW    offset NoteDelay    ; D
  2660.     DW    offset PatternDelay    ; E
  2661.     DW    offset DoNothing    ; F
  2662.  
  2663.  
  2664.  
  2665. ;/***************************************************************************\
  2666. ;*
  2667. ;* Function:    int s3mSetPosition(ushort pos)
  2668. ;*
  2669. ;* Description: Jumps to a specified position in module
  2670. ;*
  2671. ;* Input:    ushort    pos        Position to jump to
  2672. ;*
  2673. ;* Returns:    MIDAS error code
  2674. ;*
  2675. ;\***************************************************************************/
  2676.  
  2677. PROC    s3mSetPosition    FAR    pos : word
  2678. USES    si,di
  2679.  
  2680.     mov    bx,[pos]        ; bx = new position
  2681.     cmp    bx,0            ; is new position negative
  2682.     jge    @@noneg
  2683.     mov    bx,[songLength]     ; yes, set it to song end
  2684.     dec    bx
  2685. @@noneg:
  2686.     mov    [row],0
  2687.  
  2688.     les    si,[module]        ; point es:si to module structure
  2689. @@findpos:
  2690.     cmp    bx,[songLength]     ; past song end?
  2691.     jge    @@restart        ; if so, restart from beginning
  2692.  
  2693.     lgs    di,[es:si+mpModule.orders]
  2694.     mov    cl,[gs:di+bx]        ; cl = pattern number for this pos
  2695.     cmp    cl,0FEh         ; is pattern 0FEh?
  2696.     je    @@next            ; if is, skip
  2697.     cmp    cl,0FFh         ; is pattern 0FFh?
  2698.     je    @@next            ; if is, skip
  2699.     jmp    @@posok
  2700.  
  2701. @@next:
  2702.     inc    bx            ; next position
  2703.     jmp    @@findpos
  2704.  
  2705. @@restart:
  2706.     xor    bx,bx            ; restart song - set position to 0
  2707.     mov    [loopStart],0        ; set song loop start to 0
  2708.  
  2709. @@posok:
  2710.     mov    [position],bx        ; store new position
  2711.     mov    [poss],bx
  2712.     mov    [playOffset],2        ; skip pattern length word
  2713.     mov    [playCount],0
  2714.  
  2715.     xor    ax,ax
  2716.     ret
  2717. ENDP
  2718.  
  2719.  
  2720.  
  2721.  
  2722. ;/***************************************************************************\
  2723. ;*
  2724. ;* Function:    int s3mGetInformation(mpInformation *info);
  2725. ;*
  2726. ;* Description: Fills the Module Player information structure
  2727. ;*
  2728. ;* Input:    mpInformation *info    information structure to be filled
  2729. ;*
  2730. ;* Returns:    MIDAS error code
  2731. ;*
  2732. ;\***************************************************************************/
  2733.  
  2734. PROC    s3mGetInformation    FAR    info : dword
  2735. USES    si, di
  2736.  
  2737.     les    si,[info]        ; point es:si to info structure
  2738.     mov    di,offset channels    ; point ds:di to channel structures
  2739.  
  2740.     mov    ax,[setFrame]            ; copy set-frame flag
  2741.     mov    [es:si+mpInformation.setFrame],ax
  2742.     mov    [setFrame],0            ; and set it to zero
  2743.  
  2744.     mov    ax,[rows]            ; copy saved row, position
  2745.     mov    [es:si+mpInformation.row],ax    ; and pattern numbers
  2746.     mov    ax,[poss]
  2747.     mov    [es:si+mpInformation.pos],ax
  2748.     mov    ax,[pats]
  2749.     mov    [es:si+mpInformation.pattern],ax
  2750.  
  2751.     movzx    ax,[speed]
  2752.     mov    [es:si+mpInformation.speed],ax    ; copy speed and BPM tempo
  2753.     movzx    ax,[tempo]
  2754.     mov    [es:si+mpInformation.BPM],ax
  2755.  
  2756.     movzx    ax,[loopCnt]            ; copy song loop counter
  2757.     mov    [es:si+mpInformation.loopCnt],ax
  2758.  
  2759.     mov    cx,[es:si+mpInformation.numChannels]   ; cx = channel counter
  2760.     les    si,[es:si+mpInformation.chans]        ; point es:si to
  2761.                             ; channel info
  2762. @@chanloop:
  2763.     mov    al,[di+s3mChannel.flags]    ; copy channel flags
  2764.     mov    [es:si+mpChanInfo.flags],al
  2765.  
  2766.     mov    al,[di+s3mChannel.snote]    ; copy note number
  2767.     mov    [es:si+mpChanInfo.note],al
  2768.  
  2769.     mov    al,[di+s3mChannel.sample]    ; copy instrument number
  2770.     mov    [es:si+mpChanInfo.instrument],al
  2771.  
  2772.     mov    al,[di+s3mChannel.info]     ; copy command infobyte
  2773.     mov    [es:si+mpChanInfo.infobyte],al
  2774.  
  2775.     mov    al,[di+s3mChannel.volume]    ; copy volume
  2776.     mov    [es:si+mpChanInfo.volume],al
  2777.  
  2778.     mov    al,[di+s3mChannel.volbar]    ; copy volume bar
  2779.     mul    [masterVolume]
  2780.     shr    ax,6
  2781.     mov    [es:si+mpChanInfo.volumebar],al
  2782.  
  2783.     test    [di+s3mChannel.flags],128    ; is there a command?
  2784.     jz    @@nocmd
  2785.  
  2786.     movzx    bx,[di+s3mChannel.cmd]        ; bx = command number
  2787.  
  2788.     cmp    bx,"S"-"@"                      ; is command a S-command?
  2789.     jne    @@noscmd
  2790.  
  2791.     ; S-command
  2792.     mov    al,[es:si+mpChanInfo.infobyte]
  2793.     shr    al,4                ; command number = infobyte
  2794.     movzx    bx,al                ; upper nybble + 20h
  2795.     add    al,20h                ; infobyte = infobyte lower
  2796.     mov    [es:si+mpChanInfo.command],al    ; nybble
  2797.     and    [es:si+mpChanInfo.infobyte],0Fh
  2798.  
  2799.     shl    bx,2
  2800.     mov    eax,[scmdNames+bx]        ; eax = command name pointer
  2801.     mov    [es:si+mpChanInfo.commandname],eax    ; store name pointer
  2802.     jmp    @@cmddone
  2803.  
  2804.  
  2805. @@noscmd:
  2806.     mov    [es:si+mpChanInfo.command],bl    ; store command number
  2807.  
  2808.     shl    bx,2
  2809.     mov    eax,[cmdNames+bx]        ; eax = command name pointer
  2810.     mov    [es:si+mpChanInfo.commandname],eax    ; store name pointer
  2811.     jmp    @@cmddone
  2812.  
  2813. @@nocmd:
  2814.     ; no command:
  2815.     mov    [es:si+mpChanInfo.command],0    ; command number = 0
  2816.  
  2817.     ; point command name string to empty:
  2818.     mov    [word es:si+mpChanInfo.commandname],offset strNoCmd
  2819.     mov    [word es:si+2+mpChanInfo.commandname],seg strNoCmd
  2820.  
  2821. @@cmddone:
  2822.     add    si,SIZE mpChanInfo        ; next channel
  2823.     add    di,SIZE s3mChannel
  2824.     loop    @@chanloop
  2825.  
  2826.     xor    ax,ax
  2827.     ret
  2828. ENDP
  2829.  
  2830.  
  2831.  
  2832.  
  2833. ;/***************************************************************************\
  2834. ;*
  2835. ;* Function:    s3mSave
  2836. ;*
  2837. ;* Description: Saves row, position and pattern values for GetInformation()
  2838. ;*
  2839. ;\***************************************************************************/
  2840.  
  2841. PROC    s3mSave     NEAR
  2842.  
  2843.     mov    [setFrame],1        ; set set-frame flag
  2844.     mov    ax,[row]
  2845.     mov    [rows],ax        ; save row and position
  2846.     mov    bx,[position]
  2847.     mov    [poss],bx
  2848.  
  2849.     lgs    di,[es:si+mpModule.orders]
  2850.     movzx    ax,[gs:di+bx]        ; save pattern number
  2851.     mov    [pats],ax
  2852.  
  2853.     xor    ax,ax
  2854.     ret
  2855. ENDP
  2856.  
  2857.  
  2858.  
  2859.  
  2860. ;/***************************************************************************\
  2861. ;*
  2862. ;* Function:    s3mUpdBars
  2863. ;*
  2864. ;* Description: Updates "fake" volume bars
  2865. ;*
  2866. ;\***************************************************************************/
  2867.  
  2868. PROC    s3mUpdBars    NEAR
  2869. USES    di
  2870.  
  2871.     mov    di,offset channels    ; point ds:di to channel structures
  2872.     mov    cx,[numChans]
  2873.  
  2874. @@chanloop:
  2875.     cmp    [di+s3mChannel.volbar],0    ; is volume bar zero?
  2876.     je    @@1
  2877.     dec    [di+s3mChannel.volbar]        ; if not, decrement it
  2878. @@1:
  2879.     test    [di+s3mChannel.status],1    ; has volume been changed?
  2880.     jz    @@nochange
  2881.     mov    al,[di+s3mChannel.volume]
  2882.     test    [di+s3mChannel.status],2    ; force new volume?
  2883.     jnz    @@force
  2884.     cmp    [di+s3mChannel.volbar],al    ; do not force volume
  2885.     jbe    @@nochange            ; is bar above volume level?
  2886. @@force:
  2887.     mov    [di+s3mChannel.volbar],al    ; set new volume
  2888.  
  2889. @@nochange:
  2890.     and    [di+s3mChannel.status],not 3    ; clear volume change bits
  2891.     add    di,SIZE s3mChannel        ; next channel
  2892.     loop    @@chanloop
  2893.  
  2894.     xor    ax,ax
  2895.     ret
  2896. ENDP
  2897.  
  2898.  
  2899. ;/***************************************************************************\
  2900. ;*
  2901. ;* Function:    int s3mSetMasterVolume(uchar volume)
  2902. ;*
  2903. ;* Description: Sets the module player master volume
  2904. ;*
  2905. ;* Input:    uchar  volume          New master volume
  2906. ;*
  2907. ;* Returns:    MIDAS error code
  2908. ;*
  2909. ;\***************************************************************************/
  2910.  
  2911. PROC    s3mSetMasterVolume  FAR     vol : word
  2912.  
  2913.     mov    ax,[vol]
  2914.     cmp    al,64
  2915.     jle    @@ok
  2916.     mov    al,64
  2917. @@ok:
  2918.     mov    [masterVolume],al
  2919.     xor    ax,ax            ; success
  2920.     ret
  2921. ENDP
  2922.  
  2923.  
  2924. END
  2925.  
  2926.