?login_element?

Subversion Repositories NedoOS

Rev

Rev 1959 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. S3MMAXINSTRUMENTS = 128
  2. S3MWAVEHEADERBUFFERSIZE = S3MMAXINSTRUMENTS*MOONWAVEHEADERSIZE
  3. S3MSAMPLEDATASTART = MODSAMPLEDATASTART
  4. S3MMAXCHANNELS = 32
  5. S3MMAXPATTERNS = 100
  6. S3MPATTERNSTEPCOUNT = 64
  7. S3MMAXORDER = 256
  8. S3MHEADERADDR = 0xc000
  9. S3MMINPERIOD = 4
  10.  
  11.         struct S3MHEADER
  12. songname ds 28
  13. byte1a ds 1
  14. filetype ds 1
  15. unused1 ds 2
  16. ordernum ds 2
  17. instnum ds 2
  18. pattnum ds 2
  19. flags ds 2
  20. createdwith ds 2
  21. fileversion ds 2
  22. id ds 4
  23. globalvol ds 1
  24. initialspeed ds 1
  25. initialtempo ds 1
  26. mastermult ds 1
  27. ultraclicks ds 1
  28. panningmagic ds 1
  29. unused2 ds 8
  30. special ds 2
  31. chsettings ds S3MMAXCHANNELS
  32.         ends
  33.  
  34.         struct S3MINSTRUMENT
  35. type ds 1
  36. dosname ds 12
  37. paraptr ds 3
  38. length ds 4
  39. loopstart ds 4
  40. loopend ds 4
  41. volume ds 1
  42. disknum ds 1
  43. pack ds 1
  44. flags ds 1
  45. c2spd ds 4
  46. tunefactorcoeff ds 2
  47. tunefactorshift ds 1
  48. unused ds 9
  49. name ds 28
  50. id ds 4
  51.         ends
  52.  
  53.         struct S3MCHANNEL
  54. index ds 1
  55. instrument ds 1
  56. oldinstrument ds 1
  57. period ds 2
  58. note ds 1
  59. volume ds 1
  60. tempcommand ds 2
  61. vibratocommand ds 1
  62. vibratotableposition ds 1
  63. tremolocommand ds 1
  64. tremolotableposition ds 1
  65. wavecontrol ds 1
  66. patternloopstart ds 1
  67. patternloopcount ds 1
  68. pankeyon ds 1
  69. tunefactorcoeff ds 2
  70. tunefactorshift ds 1
  71. tunefactorcoeffoverride ds 2
  72. tunefactorshiftoverride ds 1
  73. portaspeed ds 1
  74. volumeslide ds 1
  75.         ends
  76.  
  77.         struct S3MSTEPDATA
  78. note ds 1
  79. instrument ds 1
  80. volume ds 1
  81. effectcommand ds 1
  82. effectdata ds 1
  83. extendedcommand ds 1
  84.         ends
  85.  
  86.         struct S3MINFO
  87. instparapointers ds 2
  88. pattparapointers ds 2
  89. choffset ds S3MMAXCHANNELS
  90. chpanning ds S3MMAXCHANNELS
  91. chnum ds 1
  92. chinitsize ds 2
  93.         ends
  94.  
  95.         struct S3MPLAYER
  96. patterntableindex ds 1
  97. patternstepindex ds 1
  98. arpeggio ds 1
  99. patterndelay ds 1
  100. speed ds 1
  101. speedstep ds 1
  102. channels ds S3MCHANNEL*S3MMAXCHANNELS
  103. stepdatabuffer ds S3MSTEPDATA*(S3MMAXCHANNELS+1)
  104.         ends
  105.  
  106. s3mpatterntable equ s3mheader+S3MHEADER
  107.  
  108. s3mload
  109. ;de = input file name
  110. ;out: zf=1 if the file is ready for playing, zf=0 otherwise
  111.         ld (s3mloadsamples.filename),de
  112.         call memorystreamloadfile
  113.         ret nz
  114. ;map header to S3MHEADERADDR
  115.         ld a,(memorystreampages)
  116.         SETPGC000
  117.         call opl4init
  118.         call s3mloadpatterns
  119.         jp nz,memorystreamfree ;sets zf=0
  120.         call s3mloadsamples
  121.         jp nz,memorystreamfree ;sets zf=0
  122. ;init player state
  123.         ld hl,s3mplayer
  124.         ld de,s3mplayer+1
  125.         ld bc,S3MPLAYER-1
  126.         ld (hl),0
  127.         ldir
  128. ;init channels
  129.         ld ix,s3minfo.chpanning
  130.         ld iy,s3mplayer.channels
  131.         ld a,(s3minfo.chnum)
  132.         ld b,a
  133.         ld c,0
  134. .initchloop
  135.         ld (iy+S3MCHANNEL.index),c
  136.         ld a,(ix)
  137.         call s3msetpanning
  138.         ld de,S3MCHANNEL
  139.         add iy,de
  140.         inc ix
  141.         inc c
  142.         djnz .initchloop
  143. ;init player state
  144.         ld a,(s3mheader.initialtempo)
  145.         call s3msetbpm
  146.         xor a
  147.         call s3msetnextstep
  148.         ld a,(s3mheader.initialspeed)
  149.         ld (s3mplayer.speed),a
  150.         ld a,1
  151.         ld (s3mplayer.speedstep),a
  152.         xor a
  153.         ld (s3mheader.byte1a),a
  154.         ret
  155.  
  156. s3mloadpatterns
  157.         call getpanningfunc
  158.         ld (.getpanning),hl
  159. ;parapointers
  160.         ld a,(s3mheader.ordernum)
  161.         ld e,a
  162.         ld d,0
  163.         ld hl,s3mpatterntable
  164.         add hl,de
  165.         ld (s3minfo.instparapointers),hl
  166.         ld a,(s3mheader.instnum)
  167.         ld e,a
  168.         add hl,de
  169.         add hl,de
  170.         ld (s3minfo.pattparapointers),hl
  171. ;fill channel tables
  172.         ld a,(s3mheader.pattnum)
  173.         ld e,a
  174.         add hl,hl
  175.         add hl,hl
  176.         ld de,s3minfo.choffset
  177.         ld bc,s3minfo.chpanning
  178.         ld ix,s3mheader.chsettings
  179.         ld iy,S3MMAXCHANNELS
  180.         xor a
  181.         ex af,af'
  182. .chloop ld a,(ix)
  183.         cp 16
  184.         jr nc,.choff
  185. .getpanning=$+1
  186.         call 0
  187.         ld (bc),a
  188.         inc bc
  189.         ex af,af'
  190.         ld (de),a
  191.         add a,S3MSTEPDATA
  192.         ex af,af'
  193.         inc de
  194.         inc iyh
  195.         ld a,iyh
  196.         cp OPL4MAXWAVECHANNELS
  197.         jr nc,.channelscapped
  198. .choff  inc hl
  199.         inc ix
  200.         dec iyl
  201.         jr nz,.chloop
  202.         ld a,iyh
  203. .channelscapped
  204.         ld (s3minfo.chnum),a
  205.         ld a,S3MMAXCHANNELS+1
  206.         sub iyh
  207.         ld b,a
  208.         ex af,af'
  209.         dec b
  210.         jr z,.nofill
  211.         ld (de),a
  212.         inc de
  213.         djnz $-2
  214. .nofill cp S3MSTEPDATA*2
  215.         jr nc,$+4
  216.         ld a,S3MSTEPDATA*2
  217.         sub S3MSTEPDATA
  218.         ld l,a
  219.         ld h,0
  220.         ld (s3minfo.chinitsize),hl
  221.         xor a
  222.         ret
  223.  
  224. getpanningfunc
  225.         ld a,(s3mheader.panningmagic)
  226.         cp 252
  227.         ld hl,.readdefaultpanning
  228.         ret z
  229.         ld hl,.chsetttingspanning
  230.         ret
  231. .chsetttingspanning
  232.         cp 8
  233.         ld a,0x03
  234.         ret c
  235.         ld a,0x0c
  236.         ret
  237. .readdefaultpanning
  238.         ld a,(hl)
  239.         and 15
  240.         ret
  241.  
  242. s3mfilestreamseekfast
  243. ;dehl = offset
  244.         ld (.fileoffsetlo),hl
  245.         ld (.fileoffsethi),de
  246.         ld hl,(filestreamcurrentaddr)
  247.         res 6,h
  248.         res 7,h
  249.         ld de,(filereadoffset+0)
  250.         add hl,de
  251.         ld bc,(filereadoffset+2)
  252.         jr nc,$+3
  253.         inc bc
  254.         ex de,hl
  255. .fileoffsetlo=$+1
  256.         ld hl,0
  257.         sub hl,de
  258.         ex de,hl
  259. .fileoffsethi=$+1
  260.         ld hl,0
  261.         sbc hl,bc
  262.         ld a,h
  263.         or l
  264.         or d
  265.         ld b,e
  266.         ld hl,(.fileoffsetlo)
  267.         ld de,(.fileoffsethi)
  268.         jp nz,s3mfilestreamseek
  269.         inc b
  270.         dec b
  271.         ret z
  272.         ld hl,(filestreamcurrentaddr)
  273. .loop   bit 6,h
  274.         call nz,s3mloadfiledata
  275.         inc hl
  276.         djnz .loop
  277.         ld (filestreamcurrentaddr),hl
  278.         ret
  279.  
  280. s3mfilestreamseek
  281. ;dehl = offset
  282.         push ix
  283.         push iy
  284.         ld a,(filehandle)
  285.         ld b,a
  286.         OS_SEEKHANDLE
  287.         ld hl,0xffff
  288.         ld (filestreamcurrentaddr),hl
  289.         pop iy
  290.         pop ix
  291.         ret
  292.  
  293. s3mloadfiledata
  294.         push af,bc,de
  295.         exx
  296.         ex af,af'
  297.         push af,bc,de,hl,ix,iy
  298.         ld a,(filehandle)
  299.         ld b,a
  300.         OS_TELLHANDLE
  301.         ld (filereadoffset+0),hl
  302.         ld (filereadoffset+2),de
  303.         ld de,0x8000
  304.         ld hl,0x4000
  305.         call readstream_file
  306.         pop iy,ix,hl,de,bc,af
  307.         exx
  308.         ex af,af'
  309.         pop de,bc,af
  310.         ld hl,0x8000
  311.         ret
  312.  
  313. filestreamcurrentaddr ds 2
  314. filereadoffset ds 4
  315.  
  316. s3mloadsamples
  317. ;output: zf=1 if samples are loaded, zf=0 otherwise
  318. .filename=$+1
  319.         ld de,0
  320.         call openstream_file
  321.         or a
  322.         ret nz
  323.         ld hl,0
  324.         ld (filestreamcurrentaddr),hl
  325.         dec hl
  326.         ld (filereadoffset+0),hl
  327.         ld (filereadoffset+2),hl
  328.         ld a,(modfilebufferpage)
  329.         SETPG8000
  330. ;read samples data from file
  331.         ld hl,S3MSAMPLEDATASTART%65536
  332.         ld a,S3MSAMPLEDATASTART/65536
  333.         ld (.sampleaddresslo),hl
  334.         ld (.sampleaddresshi),a
  335.         ld hl,(s3minfo.instparapointers)
  336.         ld iy,s3mwaveheaderbuffer
  337.         ld a,(s3mheader.instnum)
  338.         ld b,a
  339. .mainloop
  340.         push bc
  341. ;convert instrument parapointer to memory address
  342.         ld e,(hl)
  343.         inc hl
  344.         ld d,(hl)
  345.         dec hl
  346.         ex de,hl
  347.         add hl,hl
  348.         add hl,hl
  349.         add hl,hl
  350.         add hl,hl
  351.         ld bc,S3MHEADERADDR
  352.         add hl,bc
  353.         ex de,hl
  354.         ld ix,de
  355. ;replace parapointer with pointer
  356.         ld (hl),e
  357.         inc hl
  358.         ld (hl),d
  359.         inc hl
  360.         push hl
  361. ;start filling wavetable entry
  362.         bit 0,(ix+S3MINSTRUMENT.flags)
  363.         jr z,.noloop
  364.         ld de,(ix+S3MINSTRUMENT.loopend)
  365.         ld bc,(ix+S3MINSTRUMENT.loopstart)
  366.         ld hl,de
  367.         dec de
  368.         sub hl,bc
  369.         jr nz,.hasloop
  370. .noloop
  371.         ld bc,(ix+S3MINSTRUMENT.length)
  372.         ld de,bc
  373.         dec bc
  374. .hasloop
  375.         ld hl,0xffff
  376.         sub hl,de
  377.         ld (iy+ 3),b ;loop hi
  378.         ld (iy+ 4),c ;loop lo
  379.         ld (iy+ 5),h ;end hi
  380.         ld (iy+ 6),l ;end lo
  381.         ld (iy+ 7),0x00 ;LFO, VIB
  382.         ld (iy+ 8),0xf0 ;AR, D1R
  383.         ld (iy+ 9),0xff ;DL, D2R
  384.         ld (iy+10),0x0f ;rate correction, RR
  385.         ld (iy+11),0x00 ;AM
  386. .sampleaddresslo=$+1
  387.         ld hl,0
  388. .sampleaddresshi=$+1
  389.         ld d,0
  390.         ld a,(ix+S3MINSTRUMENT.flags)
  391.         rrca
  392.         rrca
  393.         rrca
  394.         and 0x80
  395.         or d
  396.         ld (iy+0),a ;8/16 bits data, addr hi
  397.         ld (iy+1),h ;addr mi
  398.         ld (iy+2),l ;addr lo
  399.         ld bc,(ix+S3MINSTRUMENT.length)
  400.         ld a,b
  401.         or c
  402.         jp z,.nextsample
  403. ;upload sample
  404.         push de
  405.         push hl
  406.         ld a,c
  407.         dec bc
  408.         inc b
  409.         ld c,b
  410.         ld b,a
  411.         push bc
  412.         call opl4setmemoryaddress
  413.         ld de,0x1102
  414.         call opl4writewave
  415. ;seek to samples data
  416.         ld d,0
  417.         ld e,(ix+S3MINSTRUMENT.paraptr+0)
  418.         ld l,(ix+S3MINSTRUMENT.paraptr+1)
  419.         ld h,(ix+S3MINSTRUMENT.paraptr+2)
  420.         add hl,hl : rl de
  421.         add hl,hl : rl de
  422.         add hl,hl : rl de
  423.         add hl,hl : rl de
  424.         call s3mfilestreamseekfast
  425.         pop bc
  426.         ld hl,(filestreamcurrentaddr)
  427. ;start uploading
  428.         opl4_wait
  429.         ld a,6
  430.         out (MOON_WREG),a
  431.         bit 2,(ix+S3MINSTRUMENT.flags)
  432.         jr nz,.uploadloop16bits
  433. .uploadloop8bits
  434.         bit 6,h
  435.         call nz,s3mloadfiledata
  436.         ld d,(hl)
  437.         inc hl
  438.         opl4_wait
  439.         ld a,d
  440.         add a,0x80
  441.         out (MOON_WDAT),a
  442.         djnz .uploadloop8bits
  443.         dec c
  444.         jr nz,.uploadloop8bits
  445. ;duplicate the last data sample
  446.         opl4_wait
  447.         ld a,d
  448.         add a,0x80
  449.         out (MOON_WDAT),a
  450.         jr .doneupload
  451. .uploadloop16bits
  452.         bit 6,h
  453.         call nz,s3mloadfiledata
  454.         ld e,(hl)
  455.         inc hl
  456.         bit 6,h
  457.         call nz,s3mloadfiledata
  458.         ld d,(hl)
  459.         inc hl
  460.         opl4_wait
  461.         ld a,d
  462.         add a,0x80
  463.         out (MOON_WDAT),a
  464.         opl4_wait
  465.         ld a,e
  466.         out (MOON_WDAT),a
  467.         djnz .uploadloop16bits
  468.         dec c
  469.         jr nz,.uploadloop16bits
  470.         opl4_wait
  471.         ld a,d
  472.         add a,0x80
  473.         out (MOON_WDAT),a
  474.         opl4_wait
  475.         ld a,e
  476.         out (MOON_WDAT),a
  477. .doneupload
  478.         ld de,0x1002
  479.         call opl4writewave
  480.         ld (filestreamcurrentaddr),hl
  481.         pop hl
  482.         pop de
  483. ;set next write address
  484.         ld bc,(ix+S3MINSTRUMENT.length)
  485.         xor a
  486.         bit 2,(ix+S3MINSTRUMENT.flags)
  487.         jr z,.alreadyinbytes
  488.         sla bc
  489.         rla
  490. .alreadyinbytes
  491.         add hl,bc
  492.         adc a,d
  493.         ld bc,2 ; add +2 to account for duping the last data sample
  494.         add hl,bc
  495.         adc a,b
  496.         ld (.sampleaddresslo),hl
  497.         ld (.sampleaddresshi),a
  498. ;process c2spd
  499.         ld hl,(ix+S3MINSTRUMENT.c2spd)
  500.         push ix
  501.         push iy
  502.         call s3mgettunefactor
  503.         pop iy
  504.         pop ix
  505.         ld (ix+S3MINSTRUMENT.tunefactorcoeff),hl
  506.         ld (ix+S3MINSTRUMENT.tunefactorshift),a
  507. .nextsample
  508.         ld bc,MOONWAVEHEADERSIZE
  509.         add iy,bc
  510.         pop hl
  511.         pop bc
  512.         dec b
  513.         jp nz,.mainloop
  514. ;switch back to memory steam
  515.         call closestream_file
  516.         ld a,(memorystreamcurrentpage)
  517.         SETPG8000
  518. ;write headers
  519.         ld ix,s3mwaveheaderbuffer
  520.         ld hl,MOONSOUNDROMSIZE%65536
  521.         ld d,MOONSOUNDROMSIZE/65536
  522.         ld bc,S3MWAVEHEADERBUFFERSIZE
  523.         call opl4writememory
  524.         xor a
  525.         ret
  526.  
  527. s3munload
  528.         call opl4mute
  529.         jp memorystreamfree
  530.  
  531. s3mplay
  532.         call s3mwaittimer
  533.         ld a,(s3mplayer.speedstep)
  534.         dec a
  535.         jp nz,.tn
  536.         ld a,(s3mplayer.speed)
  537.         ld (s3mplayer.speedstep),a
  538.         ld a,(s3mplayer.patterndelay)
  539.         or a
  540.         jr z,.nopatterndelay
  541.         dec a
  542.         ld (s3mplayer.patterndelay),a
  543.         ret
  544. .nopatterndelay
  545.         call s3mreadstepdata
  546.         ld ix,s3mplayer.stepdatabuffer
  547.         ld iy,s3mplayer.channels
  548.         ld a,(s3minfo.chnum)
  549. .t0loop push af
  550.         ld (iy+S3MCHANNEL.tunefactorshiftoverride),0xff
  551.         call s3msetinstrument
  552.         call s3mhandlecommandT0
  553.         ld d,(ix+S3MSTEPDATA.effectcommand)
  554.         ld e,(ix+S3MSTEPDATA.extendedcommand)
  555.         ld hl,0x130d
  556.         sub hl,de
  557.         call nz,s3mnewnote
  558.         ld a,(ix+S3MSTEPDATA.volume)
  559.         cp 255
  560.         jr z,.novolumecmd
  561.         clamp_volume_in_a
  562.         ld (iy+S3MCHANNEL.volume),a
  563. .novolumecmd
  564.         ld a,(iy+S3MCHANNEL.volume)
  565.         call s3msetvolume
  566.         set 7,(iy+S3MCHANNEL.pankeyon)
  567.         call s3mflushpankeyon
  568.         ld de,S3MSTEPDATA
  569.         add ix,de
  570.         ld e,S3MCHANNEL
  571.         add iy,de
  572.         pop af
  573.         dec a
  574.         jp nz,.t0loop
  575.         ret
  576. .tn     ld (s3mplayer.speedstep),a
  577.         ld a,(s3mplayer.arpeggio)
  578.         inc a
  579.         cp 3
  580.         jr c,$+3
  581.         xor a
  582.         ld (s3mplayer.arpeggio),a
  583.         ld ix,s3mplayer.stepdatabuffer
  584.         ld iy,s3mplayer.channels
  585.         ld a,(s3minfo.chnum)
  586.         ld b,a
  587. .tnloop push bc
  588.         call s3mhandlecommandTN
  589.         ld de,S3MSTEPDATA
  590.         add ix,de
  591.         ld e,S3MCHANNEL
  592.         add iy,de
  593.         pop bc
  594.         djnz .tnloop
  595.         ret
  596.  
  597. s3msetinstrument
  598. ;ix = step data
  599. ;iy = channel data
  600.         ld a,(ix+S3MSTEPDATA.instrument)
  601.         or a
  602.         ret z
  603.         ld (iy+S3MCHANNEL.instrument),a
  604.         dec a
  605.         ld hl,(s3minfo.instparapointers)
  606.         ld e,a
  607.         ld d,0
  608.         add hl,de
  609.         add hl,de
  610.         ld e,(hl)
  611.         inc hl
  612.         ld d,(hl)
  613.         push ix
  614.         ld ix,de
  615.         ld de,(ix+S3MINSTRUMENT.tunefactorcoeff)
  616.         ld (iy+S3MCHANNEL.tunefactorcoeff),de
  617.         ld a,(ix+S3MINSTRUMENT.tunefactorshift)
  618.         ld (iy+S3MCHANNEL.tunefactorshift),a
  619.         ld a,(ix+S3MINSTRUMENT.volume)
  620.         ld (iy+S3MCHANNEL.volume),a
  621.         ld (iy+S3MCHANNEL.vibratotableposition),0
  622.         pop ix
  623.         ret
  624.  
  625. s3mhandlecommandT0
  626. ;ix = step data
  627. ;iy = channel data
  628.         ld a,(ix+S3MSTEPDATA.effectcommand)
  629.         cp 23
  630.         ret nc
  631.         ld b,a
  632.         add a,a
  633.         add a,b
  634.         ld (.effectcommandtable),a
  635. .effectcommandtable=$+1
  636.         jr $
  637.         ret : ds 2          ;  0 -
  638.         jp .doeffectA       ;  1 [Set Speed]
  639.         ret : ds 2          ;  2 [Pattern Jump]
  640.         ret : ds 2          ;  3 [Pattern Break]
  641.         jp s3mvolumeslideT0 ;  4 [Volume Slide/Fine Volume Slide up/down]
  642.         jp .doeffectE       ;  5 [Porta Down/Fine Porta Down/Xtra Fine Porta]
  643.         jp .doeffectF       ;  6 [Porta Up/Fine Porta Up/Extra Fine Porta Down]
  644.         ret : ds 2          ;  7 [Porta to note]
  645.         jp .doeffectH       ;  8 [Vibrato]
  646.         ret : ds 2          ;  9 [Tremor]
  647.         jp .doeffectJ       ; 10 [Arpeggio]
  648.         jp s3mvolumeslideT0 ; 11 [Vibrato+Volume Slide]
  649.         jp s3mvolumeslideT0 ; 12 [Porta+Volume Slide]
  650.         ret : ds 2          ; 13 -
  651.         ret : ds 2          ; 14 -
  652.         ret : ds 2          ; 15 [Sample Offset]
  653.         ret : ds 2          ; 16 -
  654.         ret : ds 2          ; 17 [Retrig + Volume Slide]
  655.         jp .doeffectR       ; 18 [Tremolo]
  656.         jp .doeffectS       ; 19 [Extended Command]
  657.         jp .doeffectT       ; 20 [Set Tempo]
  658.         jp .doeffectU       ; 21 [Fine Vibrato]
  659. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 [Global Volume]
  660.         ret
  661. .doeffectR
  662.         ld b,(ix+S3MSTEPDATA.effectdata)
  663.         ld c,(iy+S3MCHANNEL.tremolocommand)
  664.         ld a,b
  665.         and 0xf0
  666.         jr z,.skiptremolohi
  667.         xor c
  668.         and 0xf0
  669.         xor c
  670.         ld c,a
  671. .skiptremolohi
  672.         ld a,b
  673.         and 15
  674.         jr z,.skiptremololo
  675.         xor c
  676.         and 15
  677.         xor c
  678.         ld c,a
  679. .skiptremololo
  680.         ld (iy+S3MCHANNEL.tremolocommand),c
  681.         ret
  682. .doeffectJ
  683.         xor a
  684.         ld (s3mplayer.arpeggio),a
  685.         ld hl,(iy+S3MCHANNEL.period)
  686.         jp s3msetfrequency
  687. .doeffectE
  688.         ld a,(ix+S3MSTEPDATA.effectdata)
  689.         or a
  690.         jr z,$+5
  691.         ld (iy+S3MCHANNEL.portaspeed),a
  692.         ld a,(iy+S3MCHANNEL.portaspeed)
  693.         cp 0xe0
  694.         ret c
  695.         ld b,a
  696.         and 15
  697.         ld e,a
  698.         ld d,0
  699.         bit 4,b
  700.         jp z,s3mportadown.slide
  701.         ex de,hl
  702.         add hl,hl
  703.         add hl,hl
  704.         ex de,hl
  705.         jp s3mportadown.slide
  706. .doeffectF
  707.         ld a,(ix+S3MSTEPDATA.effectdata)
  708.         or a
  709.         jr z,$+5
  710.         ld (iy+S3MCHANNEL.portaspeed),a
  711.         ld a,(iy+S3MCHANNEL.portaspeed)
  712.         cp 0xe0
  713.         ret c
  714.         ld b,a
  715.         and 15
  716.         ld e,a
  717.         ld d,0
  718.         bit 4,b
  719.         jp z,s3mportaup.slide
  720.         ex de,hl
  721.         add hl,hl
  722.         add hl,hl
  723.         ex de,hl
  724.         jp s3mportaup.slide
  725. .doeffectH
  726. .doeffectU
  727.         ld a,(ix+S3MSTEPDATA.note)
  728.         cp 254
  729.         jr nc,.skipnote
  730.         ld bc,(iy+S3MCHANNEL.period)
  731.         ld (iy+S3MCHANNEL.tempcommand),bc
  732. .skipnote
  733.         ld b,(ix+S3MSTEPDATA.effectdata)
  734.         ld c,(iy+S3MCHANNEL.vibratocommand)
  735.         ld a,b
  736.         and 0xf0
  737.         jr z,.skipvibratohi
  738.         xor c
  739.         and 0xf0
  740.         xor c
  741.         ld c,a
  742. .skipvibratohi
  743.         ld a,b
  744.         and 15
  745.         jr z,.skipvibratolo
  746.         xor c
  747.         and 15
  748.         xor c
  749.         ld c,a
  750. .skipvibratolo
  751.         ld (iy+S3MCHANNEL.vibratocommand),c
  752.         ret
  753. .doeffectA
  754.         ld a,(ix+S3MSTEPDATA.effectdata)
  755.         ld (s3mplayer.speed),a
  756.         ret
  757. .doeffectT
  758.         ld a,(ix+S3MSTEPDATA.effectdata)
  759.         jp s3msetbpm
  760. .doeffectS
  761.         ld a,(ix+S3MSTEPDATA.extendedcommand)
  762.         ld b,a
  763.         add a,a
  764.         add a,b
  765.         ld (.exteffcommandtable),a
  766. .exteffcommandtable=$+1
  767.         jr $
  768.         ret : ds 2    ;  0 [Set Filter on/off]
  769.         ret : ds 2    ;  1 [Set Glissando on/off]
  770.         jp .doexteff2 ;  2 [Set FineTune]
  771.         jp .doexteff3 ;  3 [Set Vibrato Waveform]
  772.         jp .doexteff4 ;  4 [Set Tremolo Waveform]
  773.         ret : ds 2    ;  5 -
  774.         ret : ds 2    ;  6 -
  775.         ret : ds 2    ;  7 -
  776.         jp .doexteff8 ;  8 [Set Pan Position]
  777.         ret : ds 2    ;  9 -
  778.         jp .doexteffA ; 10 [Stereo Control]
  779.         ret : ds 2    ; 11 [Pattern Loop]
  780.         jp .doexteffC ; 12 [Cut Note]
  781.         jp .doexteffD ; 13 [Delay Note]
  782.         jp .doexteffE ; 14 [Pattern Delay]
  783.         ret           ; 15 [Funk Repeat]
  784. .doexteff8
  785. .doexteffA
  786.         ld a,(ix+S3MSTEPDATA.effectdata)
  787.         jp s3msetpanning
  788. .doexteff2
  789.         ld a,(ix+S3MSTEPDATA.effectdata)
  790.         ld b,a
  791.         add a,a
  792.         add a,b
  793.         get_array_value b,s3mfinetunefactors
  794.         inc hl
  795.         ld c,(hl)
  796.         ld (iy+S3MCHANNEL.tunefactorcoeffoverride),bc
  797.         inc hl
  798.         ld a,(hl)
  799.         ld (iy+S3MCHANNEL.tunefactorshiftoverride),a
  800.         ret
  801. .doexteff3
  802.         ld a,(iy+S3MCHANNEL.wavecontrol)
  803.         ld b,(ix+S3MSTEPDATA.effectdata)
  804.         and 0xfc
  805.         or b
  806.         ld (iy+S3MCHANNEL.wavecontrol),a
  807.         ld (iy+S3MCHANNEL.vibratotableposition),0
  808.         ret
  809. .doexteff4
  810.         ld a,(iy+S3MCHANNEL.wavecontrol)
  811.         ld b,(ix+S3MSTEPDATA.effectdata)
  812.         sla b
  813.         sla b
  814.         and 0xf3
  815.         or b
  816.         ld (iy+S3MCHANNEL.wavecontrol),a
  817.         ld (iy+S3MCHANNEL.tremolotableposition),0
  818.         ret
  819. .doexteffC
  820.         ld a,(ix+S3MSTEPDATA.effectdata)
  821.         or a
  822.         jr z,.channeloff
  823.         ld (iy+S3MCHANNEL.tempcommand),a
  824.         ret
  825. .channeloff
  826.         ld (iy+S3MCHANNEL.volume),a
  827.         jp s3msetvolume
  828. .doexteffD
  829.         ld a,(ix+S3MSTEPDATA.effectdata)
  830.         or a
  831.         ret z
  832.         ld (iy+S3MCHANNEL.tempcommand),a
  833.         ret
  834. .doexteffE
  835.         ld a,(ix+S3MSTEPDATA.effectdata)
  836.         ld (s3mplayer.patterndelay),a
  837.         ret
  838.  
  839. s3mhandlecommandTN
  840. ;ix = step data
  841. ;iy = channel data
  842.         ld a,(ix+S3MSTEPDATA.effectcommand)
  843.         cp 23
  844.         ret nc
  845.         ld b,a
  846.         add a,a
  847.         add a,b
  848.         ld (.effectcommandtable),a
  849. .effectcommandtable=$+1
  850.         jr $
  851.         ret : ds 2       ;  0 -
  852.         ret : ds 2       ;  1 [Set Speed]
  853.         jp .effectB      ;  2 [Pattern Jump]
  854.         jp .effectC      ;  3 [Pattern Break]
  855.         jp s3mvolumeslideTN ; 4 [Volume Slide/Fine Volume Slide up/down]
  856.         jp s3mportadown  ;  5 [Porta Down/Fine Porta Down/Xtra Fine Porta]
  857.         jp s3mportaup    ;  6 [Porta Up/Fine Porta Up/Extra Fine Porta Down]
  858.         jp s3mtoneporta  ;  7 [Porta to note]
  859.         jp s3mvibrato    ;  8 [Vibrato]
  860.         ret : ds 2       ;  9 [Tremor]
  861.         jp .doeffectJ    ; 10 [Arpeggio]
  862.         jp .doeffectK    ; 11 [Vibrato+Volume Slide]
  863.         jp .doeffectL    ; 12 [Porta+Volume Slide]
  864.         ret : ds 2       ; 13 -
  865.         ret : ds 2       ; 14 -
  866.         ret : ds 2       ; 15 [Sample Offset]
  867.         ret : ds 2       ; 16 -
  868.         ret : ds 2       ; 17 [Retrig + Volume Slide]
  869.         jp s3mtremolo    ; 18 [Tremolo]
  870.         jp .doeffectS    ; 19 [Extended Command]
  871.         ret : ds 2       ; 20 [Set Tempo]
  872.         jp s3mfvibrato   ; 21 [Fine Vibrato]
  873. ;;;;;;;;;;;;;;;;;;;;;;;;;; 22 [Global Volume]
  874.         ret
  875. .doeffectK
  876.         call s3mvibrato
  877.         jp s3mvolumeslideTN
  878. .doeffectL
  879.         call s3mtoneporta
  880.         jp s3mvolumeslideTN
  881. .doeffectJ
  882.         ld a,(ix+S3MSTEPDATA.effectdata)
  883.         or a
  884.         ret z
  885.         ld hl,(s3mplayer.arpeggio)
  886.         dec l
  887.         jr z,.datahi
  888.         inc l
  889.         jr nz,.datalo
  890.         ld hl,(iy+S3MCHANNEL.period)
  891.         jp s3msetfrequency
  892. .datahi rrca
  893.         rrca
  894.         rrca
  895.         rrca
  896. .datalo and 15
  897.         add a,(iy+S3MCHANNEL.note)
  898.         cp 96
  899.         jr c,$+4
  900.         ld a,95
  901.         call s3mnotetoperiod
  902.         call s3mtuneperiod
  903.         jp s3msetfrequency
  904. .effectB
  905.         ld a,(s3mplayer.speedstep)
  906.         dec a
  907.         ret nz
  908.         ld a,(ix+S3MSTEPDATA.effectdata)
  909.         call s3mskipmarkers
  910.         ld (s3mplayer.patterntableindex),a
  911.         xor a
  912.         jp s3msetnextstep
  913. .effectC
  914.         ld a,(s3mplayer.speedstep)
  915.         dec a
  916.         ret nz
  917.         ld a,(s3mplayer.patterntableindex)
  918.         inc a
  919.         call s3mskipmarkers
  920.         ld (s3mplayer.patterntableindex),a
  921.         ld a,(ix+S3MSTEPDATA.effectdata)
  922.         ld b,a
  923.         and 0xf0
  924.         xor b
  925.         ld c,a
  926.         xor b
  927.         rrca
  928.         rrca
  929.         rrca
  930.         rrca
  931.         add a,a
  932.         ld b,a
  933.         add a,a
  934.         add a,a
  935.         add a,b
  936.         add a,c
  937.         jp s3msetnextstep
  938. .doeffectS
  939.         ld a,(ix+S3MSTEPDATA.extendedcommand)
  940.         ld b,a
  941.         add a,a
  942.         add a,b
  943.         ld (.exteffcommandtable),a
  944. .exteffcommandtable=$+1
  945.         jr $
  946.         ret : ds 2    ;  0 [Set Filter on/off]
  947.         ret : ds 2    ;  1 [Set Glissando on/off]
  948.         ret : ds 2    ;  2 [Set FineTune]
  949.         ret : ds 2    ;  3 [Set Vibrato Waveform]
  950.         ret : ds 2    ;  4 [Set Tremolo Waveform]
  951.         ret : ds 2    ;  5 -
  952.         ret : ds 2    ;  6 -
  953.         ret : ds 2    ;  7 -
  954.         ret : ds 2    ;  8 [Set Pan Position]
  955.         ret : ds 2    ;  9 -
  956.         ret : ds 2    ; 10 [Stereo Control]
  957.         jp .doexteffB ; 11 [Pattern Loop]
  958.         jp .doexteffC ; 12 [Cut Note]
  959.         jp .doexteffD ; 13 [Delay Note]
  960.         ret : ds 2    ; 14 [Pattern Delay]
  961.         ret           ; 15 [Funk Repeat]
  962. .doexteffB
  963.         ld a,(s3mplayer.speedstep)
  964.         dec a
  965.         ret nz
  966.         ld a,(ix+S3MSTEPDATA.effectdata)
  967.         or a
  968.         jr nz,.checkloopcount
  969.         ld a,(s3mplayer.patternstepindex)
  970.         ld (iy+S3MCHANNEL.patternloopstart),a
  971.         ret
  972. .checkloopcount
  973.         ld b,(iy+S3MCHANNEL.patternloopcount)
  974.         inc b
  975.         cp b
  976.         jr c,.restartloop
  977.         ld (iy+S3MCHANNEL.patternloopcount),b
  978.         ld a,(iy+S3MCHANNEL.patternloopstart)
  979.         jp s3msetnextstep
  980. .restartloop
  981.         ld (iy+S3MCHANNEL.patternloopcount),0
  982.         ret
  983. .doexteffC
  984.         ld a,(iy+S3MCHANNEL.tempcommand)
  985.         or a
  986.         ret z
  987.         dec a
  988.         ld (iy+S3MCHANNEL.tempcommand),a
  989.         ret nz
  990.         ld (iy+S3MCHANNEL.volume),a
  991.         jp s3msetvolume
  992. .doexteffD
  993.         ld a,(iy+S3MCHANNEL.tempcommand)
  994.         or a
  995.         ret z
  996.         dec a
  997.         ld (iy+S3MCHANNEL.tempcommand),a
  998.         ret nz
  999.         jp s3mnewnote
  1000.  
  1001. s3mnewnote
  1002. ;ix = step data
  1003. ;iy = channel data
  1004.         ld a,(iy+S3MCHANNEL.instrument)
  1005.         or a
  1006.         ret z
  1007.         ld a,(ix+S3MSTEPDATA.effectcommand)
  1008.         cp 0x07
  1009.         jr z,.effectG
  1010.         cp 0x0C
  1011.         jr z,.effectL
  1012.         ld a,(ix+S3MSTEPDATA.note)
  1013.         inc a
  1014.         ret z
  1015.         xor a
  1016.         call s3msetvolume
  1017.         res 7,(iy+S3MCHANNEL.pankeyon)
  1018.         call s3mflushpankeyon
  1019.         ld a,(ix+S3MSTEPDATA.note)
  1020.         cp 254
  1021.         jr nz,.validnote
  1022.         ld (iy+S3MCHANNEL.volume),0
  1023.         ret
  1024. .validnote
  1025.         ld (iy+S3MCHANNEL.note),a
  1026.         call s3mnotetoperiod
  1027.         call s3mtuneperiod
  1028.         ld (iy+S3MCHANNEL.period),hl
  1029.         call s3msetfrequency
  1030.         ld a,(iy+S3MCHANNEL.instrument)
  1031.         cp (iy+S3MCHANNEL.oldinstrument)
  1032.         ret z
  1033.         ld (iy+S3MCHANNEL.oldinstrument),a
  1034.         jp s3msetsamplenumber
  1035. .effectG
  1036.         ld a,(ix+S3MSTEPDATA.effectdata)
  1037.         or a
  1038.         jr z,.effectL
  1039.         ld (iy+S3MCHANNEL.portaspeed),a
  1040. .effectL
  1041.         ld a,(ix+S3MSTEPDATA.note)
  1042.         cp 254
  1043.         ret nc
  1044.         ld (iy+S3MCHANNEL.note),a
  1045.         call s3mnotetoperiod
  1046.         call s3mtuneperiod
  1047.         ld (iy+S3MCHANNEL.tempcommand),hl
  1048.         ret
  1049.  
  1050. s3mskipmarkers
  1051. ;intput: a = pattern table index
  1052. ;output: a = pattern table index pointing at non-marker
  1053.         ld hl,s3mheader.ordernum
  1054.         ld b,(hl)
  1055.         ld e,a
  1056.         ld d,0
  1057.         ld hl,s3mpatterntable
  1058.         add hl,de
  1059. .loop   cp b
  1060.         jr c,$+6
  1061.         xor a
  1062.         ld hl,s3mpatterntable
  1063.         ld c,a
  1064.         ld a,(hl)
  1065.         cp 254
  1066.         ld a,c
  1067.         ret c
  1068.         inc hl
  1069.         inc a
  1070.         jp .loop
  1071.  
  1072. s3mgetpatternpos
  1073. ;a = pattern table index
  1074. ;out: dehl = file stream position
  1075.         ld e,a
  1076.         ld d,0
  1077.         ld hl,s3mpatterntable
  1078.         add hl,de
  1079.         ld e,(hl)
  1080.         ld hl,(s3minfo.pattparapointers)
  1081.         add hl,de
  1082.         add hl,de
  1083. ;parapointer
  1084.         ld a,(hl)
  1085.         inc hl
  1086.         ld h,(hl)
  1087.         ld l,a
  1088.         xor a
  1089.         add hl,hl : rla
  1090.         add hl,hl : rla
  1091.         add hl,hl : rla
  1092.         add hl,hl : rla
  1093.         ld e,a
  1094. ;skip the first two bytes containing pattern data size
  1095.         inc l
  1096.         inc l
  1097.         ret
  1098.  
  1099. memorystreamnextpagewrap
  1100. ;wraps read address rather than resetting to 0x8000
  1101.         push hl
  1102.         call memorystreamnextpage
  1103.         pop hl
  1104.         res 6,h
  1105.         set 7,h
  1106.         ret
  1107.  
  1108. s3msetnextstep
  1109. ;a = step index
  1110. ;output: memory stream at patterntableindex/patternstepindex
  1111.         dec a ;save decremented patternstepindex to cancel out its increment in T0
  1112.         ld (s3mplayer.patternstepindex),a
  1113.         ld a,(s3mplayer.patterntableindex)
  1114.         call s3mgetpatternpos
  1115.         call memorystreamseek
  1116.         ld a,(s3mplayer.patternstepindex)
  1117.         inc a
  1118.         ret z
  1119.         ld b,a
  1120.         ld hl,(memorystreamcurrentaddr)
  1121. .steploop
  1122.         ld a,(hl)
  1123.         or a
  1124.         jr z,.nextstep
  1125. .loop   and 0xe0
  1126.         ld e,d
  1127.         add a,a
  1128.         rl e
  1129.         add a,a
  1130.         rl e
  1131.         rlca
  1132.         adc a,e
  1133.         inc a
  1134.         ld e,a
  1135.         add hl,de
  1136.         bit 6,h
  1137.         call nz,memorystreamnextpagewrap
  1138.         ld a,(hl)
  1139.         or a
  1140.         jp nz,.loop
  1141. .nextstep
  1142.         inc hl
  1143.         djnz .steploop
  1144.         ld (memorystreamcurrentaddr),hl
  1145.         ret
  1146.  
  1147. s3mreadstepdata
  1148. ;input: memory stream
  1149. ;output: filled stepdatabuffer, memory stream at next pattern step
  1150.         ld a,(s3mplayer.patternstepindex)
  1151.         inc a
  1152.         cp S3MPATTERNSTEPCOUNT
  1153.         jr c,.nextpatternstep
  1154.         ld a,(s3mplayer.patterntableindex)
  1155.         inc a
  1156.         call s3mskipmarkers
  1157.         ld (s3mplayer.patterntableindex),a
  1158.         call s3mgetpatternpos
  1159.         call memorystreamseek
  1160.         xor a
  1161. .nextpatternstep
  1162.         ld (s3mplayer.patternstepindex),a
  1163. ;set defaults
  1164.         ld ix,s3mplayer.stepdatabuffer
  1165.         ld (ix+S3MSTEPDATA.note),0xff
  1166.         ld (ix+S3MSTEPDATA.volume),0xff
  1167.         ld (ix+S3MSTEPDATA.instrument),0
  1168.         ld (ix+S3MSTEPDATA.effectcommand),0xff
  1169.         ld hl,s3mplayer.stepdatabuffer
  1170.         ld de,s3mplayer.stepdatabuffer+S3MSTEPDATA
  1171.         ld bc,(s3minfo.chinitsize)
  1172.         ldir
  1173. ;read data
  1174.         ld hl,(memorystreamcurrentaddr)
  1175.         memory_stream_read_byte a
  1176.         or a
  1177.         jr z,.skiploop
  1178. .loop   ld b,a
  1179.         and 0x1f
  1180.         ex de,hl
  1181.         get_array_value l,s3minfo.choffset
  1182.         ex de,hl
  1183.         ld d,0
  1184.         ld ix,s3mplayer.stepdatabuffer
  1185.         add ix,de
  1186.         bit 5,b
  1187.         jr z,.skipnoteinstrument
  1188.         memory_stream_read_byte a
  1189.         cp 254
  1190.         jr nc,.specialnote
  1191.         ld c,a
  1192.         rrca
  1193.         rrca
  1194.         and 0x3c
  1195.         neg
  1196.         add a,c
  1197. .specialnote
  1198.         ld (ix+S3MSTEPDATA.note),a
  1199.         memory_stream_read_byte a
  1200.         ld (ix+S3MSTEPDATA.instrument),a
  1201. .skipnoteinstrument
  1202.         bit 6,b
  1203.         jr z,.skipvolume
  1204.         memory_stream_read_byte a
  1205.         ld (ix+S3MSTEPDATA.volume),a
  1206. .skipvolume
  1207.         bit 7,b
  1208.         jr z,.skipeffect
  1209.         memory_stream_read_byte a
  1210.         memory_stream_read_byte c
  1211.         ld (ix+S3MSTEPDATA.effectcommand),a
  1212.         cp 19
  1213.         ld a,c
  1214.         jr nz,.notextended
  1215.         srl c
  1216.         srl c
  1217.         srl c
  1218.         srl c
  1219.         ld (ix+S3MSTEPDATA.extendedcommand),c
  1220.         and 15
  1221. .notextended
  1222.         ld (ix+S3MSTEPDATA.effectdata),a
  1223. .skipeffect
  1224.         memory_stream_read_byte a
  1225.         or a
  1226.         jr nz,.loop
  1227. .skiploop
  1228.         ld (memorystreamcurrentaddr),hl
  1229.         ret
  1230.  
  1231. s3mnotetoperiod
  1232. ;a = note
  1233. ;out: hl = period
  1234.         add a,a
  1235.         get_array_value a,st3periods
  1236.         inc hl
  1237.         ld h,(hl)
  1238.         ld l,a
  1239.         ret
  1240.  
  1241. s3mtuneperiod
  1242. ;iy = channel data
  1243. ;hl = period
  1244. ;out: hl = period
  1245.         ld bc,(iy+S3MCHANNEL.tunefactorcoeffoverride)
  1246.         ld a,(iy+S3MCHANNEL.tunefactorshiftoverride)
  1247.         or a
  1248.         jp p,.hasoverride
  1249.         ld bc,(iy+S3MCHANNEL.tunefactorcoeff)
  1250.         ld a,(iy+S3MCHANNEL.tunefactorshift)
  1251.         or a
  1252. .hasoverride
  1253.         push af
  1254.         ex de,hl
  1255.         call uintmul16
  1256.         pop af
  1257.         jr z,.noshift
  1258.         ld b,a
  1259. .shiftloop
  1260.         add hl,hl
  1261.         rl de
  1262.         djnz .shiftloop
  1263. .noshift
  1264.         ld hl,-S3MMINPERIOD
  1265.         add hl,de
  1266.         ex de,hl
  1267.         ret c
  1268.         ld hl,S3MMINPERIOD
  1269.         ret
  1270.  
  1271. s3mgettunefactor
  1272. ;hl = c2spd
  1273. ;output: hl = factor, a = shift
  1274. ;poor man's floating point: period*8363/c2spd = (period*factor)>>(16-shift)
  1275.         ld de,0
  1276.         exx
  1277.         ld de,8363
  1278.         ld hl,0
  1279.         call uintdiv32
  1280.         ld a,d
  1281.         or e
  1282.         ret z
  1283.         ex de,hl
  1284.         push hl
  1285.         ld b,16
  1286.         add hl,hl
  1287.         dec b
  1288.         jr nc,$-2
  1289.         ld a,h
  1290.         or l
  1291.         jr z,$+3
  1292.         inc b
  1293.         inc b
  1294.         pop hl
  1295.         ex de,hl
  1296.         ld a,b
  1297. .shiftloop
  1298.         srl de
  1299.         rr hl
  1300.         djnz .shiftloop
  1301.         ret
  1302.  
  1303. s3msetbpm equ modsetbpm
  1304. s3mwaittimer equ modwaittimer
  1305.  
  1306. s3msetfrequency
  1307. ;iy = channel data
  1308. ;hl = period
  1309.         ld a,(modperiodlookuppage)
  1310.         SETPGC000
  1311.         ld a,h
  1312.         sub 0x10
  1313.         jp c,.firsthalf
  1314. ;(hl-4096)/8+4096
  1315.         srl a : rr l
  1316.         srl a : rr l
  1317.         srl a : rr l
  1318.         add a,0x10
  1319.         ld h,a
  1320. .firsthalf
  1321.         add hl,hl
  1322. ;two-bytes lookup
  1323.         ld de,S3MHEADERADDR-2
  1324.         add hl,de
  1325.         ld d,(hl)
  1326.         inc hl
  1327.         ld l,(hl)
  1328.         ld a,(memorystreampages)
  1329.         SETPGC000
  1330.         ld a,(iy+S3MCHANNEL.index)
  1331.         add a,0x38
  1332.         ld e,a
  1333.         call opl4writewave
  1334.         ld d,l
  1335.         ld a,e
  1336.         sub 0x18
  1337.         ld e,a
  1338.         jp opl4writewave
  1339.  
  1340. s3mportaup
  1341. ;iy = channel data
  1342.         ld a,(iy+S3MCHANNEL.portaspeed)
  1343.         cp 0xe0
  1344.         ret nc
  1345.         rlca
  1346.         rlca
  1347.         ld d,a
  1348.         and 0xfc
  1349.         ld e,a
  1350.         xor d
  1351.         ld d,a
  1352. .slide  ld hl,(iy+S3MCHANNEL.period)
  1353.         sub hl,de
  1354.         jr c,.clamp
  1355.         ex de,hl
  1356.         ld hl,-S3MMINPERIOD
  1357.         add hl,de
  1358.         ex de,hl
  1359.         jr c,$+5
  1360. .clamp  ld hl,S3MMINPERIOD
  1361.         ld (iy+S3MCHANNEL.period),hl
  1362.         jp s3msetfrequency
  1363.  
  1364. s3mportadown
  1365. ;iy = channel data
  1366.         ld a,(iy+S3MCHANNEL.portaspeed)
  1367.         cp 0xe0
  1368.         ret nc
  1369.         rlca
  1370.         rlca
  1371.         ld d,a
  1372.         and 0xfc
  1373.         ld e,a
  1374.         xor d
  1375.         ld d,a
  1376. .slide  ld hl,(iy+S3MCHANNEL.period)
  1377.         add hl,de
  1378.         ex de,hl
  1379.         ld hl,-36000
  1380.         add hl,de
  1381.         ex de,hl
  1382.         jr nc,$+5
  1383.         ld hl,35999
  1384.         ld (iy+S3MCHANNEL.period),hl
  1385.         jp s3msetfrequency
  1386.  
  1387. s3mtoneporta
  1388. ;iy = channel data
  1389.         ld hl,(iy+S3MCHANNEL.period)
  1390.         ld bc,(iy+S3MCHANNEL.tempcommand)
  1391.         ld de,hl
  1392.         sub hl,bc
  1393.         ex de,hl
  1394.         jp z,s3msetfrequency ;period == tempcommand
  1395.         ld e,(iy+S3MCHANNEL.portaspeed)
  1396.         ld d,0
  1397.         jr c,.pospitchbend ;period < tempcommand
  1398.         sla de
  1399.         sla de
  1400.         sbc hl,de
  1401.         jr nc,$+5
  1402.         ld hl,0
  1403.         ld de,hl
  1404.         sub hl,bc
  1405.         ex de,hl
  1406.         jr nc,.finalize ;period >= tempcommand
  1407.         ld hl,bc
  1408.         jr .finalize
  1409. .pospitchbend
  1410.         sla de
  1411.         sla de
  1412.         add hl,de
  1413.         ld de,hl
  1414.         sub hl,bc
  1415.         ex de,hl
  1416.         jr c,$+4 ;period < tempcommand
  1417.         ld hl,bc
  1418. .finalize
  1419.         ld (iy+S3MCHANNEL.period),hl
  1420.         jp s3msetfrequency
  1421.  
  1422.         macro s3m_vibrato is_fine
  1423.         ld a,(iy+S3MCHANNEL.vibratotableposition)
  1424.         rrca
  1425.         rrca
  1426.         and 0x1f
  1427.         get_array_value e,modvibratotable
  1428.         ld hl,0
  1429.         ld a,(iy+S3MCHANNEL.vibratocommand)
  1430.         and 15
  1431.         call nz,mul8x8
  1432.         xor a
  1433.         add hl,hl : rla
  1434.         if !is_fine
  1435.         add hl,hl : rla
  1436.         add hl,hl : rla
  1437.         endif
  1438.         ld e,h
  1439.         ld d,a
  1440.         ld hl,(iy+S3MCHANNEL.period)
  1441.         bit 7,(iy+S3MCHANNEL.vibratotableposition)
  1442.         jr z,.periodup
  1443.         sub hl,de
  1444.         jr .finalize
  1445. .periodup
  1446.         add hl,de
  1447. .finalize
  1448.         ld (iy+S3MCHANNEL.tempcommand),hl
  1449.         call s3msetfrequency
  1450.         ld a,(iy+S3MCHANNEL.vibratocommand)
  1451.         rrca
  1452.         rrca
  1453.         and 0x3c
  1454.         add a,(iy+S3MCHANNEL.vibratotableposition)
  1455.         ld (iy+S3MCHANNEL.vibratotableposition),a
  1456.         endm
  1457.  
  1458. s3mfvibrato
  1459. ;iy = channel data
  1460.         s3m_vibrato 1
  1461.         ret
  1462.  
  1463. s3mvibrato
  1464. ;iy = channel data
  1465.         s3m_vibrato 0
  1466.         ret
  1467.  
  1468. s3msetvolume
  1469. ;iy = channel data
  1470. ;a = volume
  1471.         get_array_value d,modvolumetable
  1472.         sll d
  1473.         ld a,(iy+S3MCHANNEL.index)
  1474.         add a,0x50
  1475.         ld e,a
  1476.         jp opl4writewave
  1477.  
  1478. s3msetsamplenumber
  1479. ;iy = channel data
  1480. ;a = sample number
  1481.         add a,0x7f
  1482.         ld d,a
  1483.         ld a,(iy+S3MCHANNEL.index)
  1484.         add a,0x08
  1485.         ld e,a
  1486.         call opl4writewave
  1487. ;wait for the header to load
  1488.         in a,(MOON_STAT)
  1489.         and 3
  1490.         jr nz,$-4
  1491.         ret
  1492.  
  1493. s3mflushpankeyon
  1494. ;iy = channel data
  1495.         ld d,(iy+S3MCHANNEL.pankeyon)
  1496.         ld a,(iy+S3MCHANNEL.index)
  1497.         add a,0x68
  1498.         ld e,a
  1499.         jp opl4writewave
  1500.  
  1501. s3mvolumeslideT0
  1502. ;ix = step data
  1503. ;iy = channel data
  1504.         ld a,(ix+S3MSTEPDATA.effectdata)
  1505.         or a
  1506.         jr z,$+5
  1507.         ld (iy+S3MCHANNEL.volumeslide),a
  1508.         ld a,(iy+S3MCHANNEL.volumeslide)
  1509.         cp 0xf0
  1510.         jr c,.checkslideup
  1511.         and 15
  1512.         ld b,a
  1513.         ld a,(iy+S3MCHANNEL.volume)
  1514.         sub b
  1515.         jr nc,$+3
  1516.         xor a
  1517.         ld (iy+S3MCHANNEL.volume),a
  1518.         jp s3msetvolume
  1519. .checkslideup
  1520.         rrca
  1521.         rrca
  1522.         rrca
  1523.         rrca
  1524.         cp 0xf0
  1525.         ret c
  1526.         and 15
  1527.         add a,(iy+S3MCHANNEL.volume)
  1528.         clamp_volume_in_a
  1529.         ld (iy+S3MCHANNEL.volume),a
  1530.         jp s3msetvolume
  1531.  
  1532. s3mvolumeslideTN
  1533. ;iy = channel data
  1534.         ld a,(iy+S3MCHANNEL.volumeslide)
  1535.         cp 0x10
  1536.         jr nc,.checkslideup
  1537.         ld b,a
  1538.         ld a,(iy+S3MCHANNEL.volume)
  1539.         sub b
  1540.         jr nc,$+3
  1541.         xor a
  1542.         ld (iy+S3MCHANNEL.volume),a
  1543.         jp s3msetvolume
  1544. .checkslideup
  1545.         rrca
  1546.         rrca
  1547.         rrca
  1548.         rrca
  1549.         cp 0x10
  1550.         ret nc
  1551.         add a,(iy+S3MCHANNEL.volume)
  1552.         clamp_volume_in_a
  1553.         ld (iy+S3MCHANNEL.volume),a
  1554.         jp s3msetvolume
  1555.  
  1556. s3msetpanning
  1557. ;iy = channel data
  1558. ;a = panning
  1559.         get_array_value l,modpantable
  1560.         ld a,(iy+S3MCHANNEL.pankeyon)
  1561.         and 0x80
  1562.         or l
  1563.         ld (iy+S3MCHANNEL.pankeyon),a
  1564.         ret
  1565.  
  1566. s3mtremolo
  1567. ;iy = channel data
  1568.         ld a,(iy+S3MCHANNEL.tremolotableposition)
  1569.         rrca
  1570.         rrca
  1571.         and 0x1f
  1572.         get_array_value e,modvibratotable
  1573.         ld hl,0
  1574.         ld a,(iy+S3MCHANNEL.tremolocommand)
  1575.         and 15
  1576.         call nz,mul8x8
  1577.         ld a,h
  1578.         sla l
  1579.         rla
  1580.         sla l
  1581.         rla
  1582.         bit 7,(iy+S3MCHANNEL.tremolotableposition)
  1583.         jr z,.volumeup
  1584.         ld b,a
  1585.         ld a,(iy+S3MCHANNEL.volume)
  1586.         sub b
  1587.         jr nc,.finalize
  1588.         xor a
  1589.         jr .finalize
  1590. .volumeup
  1591.         add a,(iy+S3MCHANNEL.volume)
  1592.         clamp_volume_in_a
  1593. .finalize
  1594.         call s3msetvolume
  1595.         ld a,(iy+S3MCHANNEL.tremolocommand)
  1596.         rrca
  1597.         rrca
  1598.         and 0x3c
  1599.         add a,(iy+S3MCHANNEL.tremolotableposition)
  1600.         ld (iy+S3MCHANNEL.tremolotableposition),a
  1601.         ret
  1602.  
  1603. s3mfinetunefactors
  1604.         db 0x87,0x96,0x01 ;7895
  1605.         db 0x86,0xcd,0x01 ;7941
  1606.         db 0x86,0x0f,0x01 ;7985
  1607.         db 0x85,0x0b,0x01 ;8046
  1608.         db 0x84,0x0a,0x01 ;8107
  1609.         db 0x83,0x0a,0x01 ;8169
  1610.         db 0x82,0x09,0x01 ;8232
  1611.         db 0x81,0x48,0x01 ;8280
  1612.         db 0x80,0x00,0x01 ;8363 (No finetune)
  1613.         db 0xfe,0x7a,0x00 ;8413
  1614.         db 0xfc,0xf9,0x00 ;8463
  1615.         db 0xfb,0x04,0x00 ;8529
  1616.         db 0xf9,0x7f,0x00 ;8581
  1617.         db 0xf7,0x7a,0x00 ;8651
  1618.         db 0xf5,0x6f,0x00 ;8723
  1619.         db 0xf4,0x7b,0x00 ;8757
  1620.  
  1621. st3periods
  1622.         ;    C     C#    D     D#    E     F     F#    G     G#    A     A#    B
  1623.         dw 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496 ;0
  1624.         dw 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248 ;1
  1625.         dw  6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624 ;2
  1626.         dw  3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812 ;3
  1627.         dw  1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016,  960,  906 ;4
  1628.         dw   856,  808,  762,  720,  678,  640,  604,  570,  538,  508,  480,  453 ;5
  1629.         dw   428,  404,  381,  360,  339,  320,  302,  285,  269,  254,  240,  226 ;6
  1630.         dw   214,  202,  190,  180,  170,  160,  151,  143,  135,  127,  120,  113 ;7
  1631.         dw   107,  101,   95,   90,   85,   80,   75,   71,   67,   63,   60,   56 ;8
  1632. st3periodtablesize=($-st3periods)/2
  1633.