Subversion Repositories NedoOS

Rev

Rev 2272 | Blame | Compare with Previous | Last modification | View Log | Download

  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.         ret z
  979.         ld a,(iy+S3MCHANNEL.patternloopstart)
  980.         jp s3msetnextstep
  981. .restartloop
  982.         ld (iy+S3MCHANNEL.patternloopcount),0
  983.         ld a,(iy+S3MCHANNEL.patternloopstart)
  984.         jp s3msetnextstep
  985. .doexteffC
  986.         ld a,(iy+S3MCHANNEL.tempcommand)
  987.         or a
  988.         ret z
  989.         dec a
  990.         ld (iy+S3MCHANNEL.tempcommand),a
  991.         ret nz
  992.         ld (iy+S3MCHANNEL.volume),a
  993.         jp s3msetvolume
  994. .doexteffD
  995.         ld a,(iy+S3MCHANNEL.tempcommand)
  996.         or a
  997.         ret z
  998.         dec a
  999.         ld (iy+S3MCHANNEL.tempcommand),a
  1000.         ret nz
  1001.         jp s3mnewnote
  1002.  
  1003. s3mnewnote
  1004. ;ix = step data
  1005. ;iy = channel data
  1006.         ld a,(iy+S3MCHANNEL.instrument)
  1007.         or a
  1008.         ret z
  1009.         ld a,(ix+S3MSTEPDATA.effectcommand)
  1010.         cp 0x07
  1011.         jr z,.effectG
  1012.         cp 0x0C
  1013.         jr z,.effectL
  1014.         ld a,(ix+S3MSTEPDATA.note)
  1015.         inc a
  1016.         ret z
  1017.         xor a
  1018.         call s3msetvolume
  1019.         res 7,(iy+S3MCHANNEL.pankeyon)
  1020.         call s3mflushpankeyon
  1021.         ld a,(ix+S3MSTEPDATA.note)
  1022.         cp 254
  1023.         jr nz,.validnote
  1024.         ld (iy+S3MCHANNEL.volume),0
  1025.         ret
  1026. .validnote
  1027.         ld (iy+S3MCHANNEL.note),a
  1028.         call s3mnotetoperiod
  1029.         call s3mtuneperiod
  1030.         ld (iy+S3MCHANNEL.period),hl
  1031.         call s3msetfrequency
  1032.         ld a,(iy+S3MCHANNEL.instrument)
  1033.         cp (iy+S3MCHANNEL.oldinstrument)
  1034.         ret z
  1035.         ld (iy+S3MCHANNEL.oldinstrument),a
  1036.         jp s3msetsamplenumber
  1037. .effectG
  1038.         ld a,(ix+S3MSTEPDATA.effectdata)
  1039.         or a
  1040.         jr z,.effectL
  1041.         ld (iy+S3MCHANNEL.portaspeed),a
  1042. .effectL
  1043.         ld a,(ix+S3MSTEPDATA.note)
  1044.         cp 254
  1045.         ret nc
  1046.         ld (iy+S3MCHANNEL.note),a
  1047.         call s3mnotetoperiod
  1048.         call s3mtuneperiod
  1049.         ld (iy+S3MCHANNEL.tempcommand),hl
  1050.         ret
  1051.  
  1052. s3mskipmarkers
  1053. ;intput: a = pattern table index
  1054. ;output: a = pattern table index pointing at non-marker
  1055.         ld hl,s3mheader.ordernum
  1056.         ld b,(hl)
  1057.         ld e,a
  1058.         ld d,0
  1059.         ld hl,s3mpatterntable
  1060.         add hl,de
  1061. .loop   cp b
  1062.         jr c,$+6
  1063.         xor a
  1064.         ld hl,s3mpatterntable
  1065.         ld c,a
  1066.         ld a,(hl)
  1067.         cp 254
  1068.         ld a,c
  1069.         ret c
  1070.         inc hl
  1071.         inc a
  1072.         jp .loop
  1073.  
  1074. s3mgetpatternpos
  1075. ;a = pattern table index
  1076. ;out: dehl = file stream position
  1077.         ld e,a
  1078.         ld d,0
  1079.         ld hl,s3mpatterntable
  1080.         add hl,de
  1081.         ld e,(hl)
  1082.         ld hl,(s3minfo.pattparapointers)
  1083.         add hl,de
  1084.         add hl,de
  1085. ;parapointer
  1086.         ld a,(hl)
  1087.         inc hl
  1088.         ld h,(hl)
  1089.         ld l,a
  1090.         xor a
  1091.         add hl,hl : rla
  1092.         add hl,hl : rla
  1093.         add hl,hl : rla
  1094.         add hl,hl : rla
  1095.         ld e,a
  1096. ;skip the first two bytes containing pattern data size
  1097.         inc l
  1098.         inc l
  1099.         ret
  1100.  
  1101. memorystreamnextpagewrap
  1102. ;wraps read address rather than resetting to 0x8000
  1103.         push hl
  1104.         call memorystreamnextpage
  1105.         pop hl
  1106.         res 6,h
  1107.         set 7,h
  1108.         ret
  1109.  
  1110. s3msetnextstep
  1111. ;a = step index
  1112. ;output: memory stream at patterntableindex/patternstepindex
  1113.         dec a ;save decremented patternstepindex to cancel out its increment in T0
  1114.         ld (s3mplayer.patternstepindex),a
  1115.         ld a,(s3mplayer.patterntableindex)
  1116.         call s3mgetpatternpos
  1117.         call memorystreamseek
  1118.         ld a,(s3mplayer.patternstepindex)
  1119.         inc a
  1120.         ret z
  1121.         ld b,a
  1122.         ld hl,(memorystreamcurrentaddr)
  1123. .steploop
  1124.         ld a,(hl)
  1125.         or a
  1126.         jr z,.nextstep
  1127. .loop   and 0xe0
  1128.         ld e,d
  1129.         add a,a
  1130.         rl e
  1131.         add a,a
  1132.         rl e
  1133.         rlca
  1134.         adc a,e
  1135.         inc a
  1136.         ld e,a
  1137.         add hl,de
  1138.         bit 6,h
  1139.         call nz,memorystreamnextpagewrap
  1140.         ld a,(hl)
  1141.         or a
  1142.         jp nz,.loop
  1143. .nextstep
  1144.         inc hl
  1145.         djnz .steploop
  1146.         ld (memorystreamcurrentaddr),hl
  1147.         ret
  1148.  
  1149. s3mreadstepdata
  1150. ;input: memory stream
  1151. ;output: filled stepdatabuffer, memory stream at next pattern step
  1152.         ld a,(s3mplayer.patternstepindex)
  1153.         inc a
  1154.         cp S3MPATTERNSTEPCOUNT
  1155.         jr c,.nextpatternstep
  1156.         ld a,(s3mplayer.patterntableindex)
  1157.         inc a
  1158.         call s3mskipmarkers
  1159.         ld (s3mplayer.patterntableindex),a
  1160.         call s3mgetpatternpos
  1161.         call memorystreamseek
  1162.         xor a
  1163. .nextpatternstep
  1164.         ld (s3mplayer.patternstepindex),a
  1165. ;set defaults
  1166.         ld ix,s3mplayer.stepdatabuffer
  1167.         ld (ix+S3MSTEPDATA.note),0xff
  1168.         ld (ix+S3MSTEPDATA.volume),0xff
  1169.         ld (ix+S3MSTEPDATA.instrument),0
  1170.         ld (ix+S3MSTEPDATA.effectcommand),0xff
  1171.         ld hl,s3mplayer.stepdatabuffer
  1172.         ld de,s3mplayer.stepdatabuffer+S3MSTEPDATA
  1173.         ld bc,(s3minfo.chinitsize)
  1174.         ldir
  1175. ;read data
  1176.         ld hl,(memorystreamcurrentaddr)
  1177.         memory_stream_read_byte a
  1178.         or a
  1179.         jr z,.skiploop
  1180. .loop   ld b,a
  1181.         and 0x1f
  1182.         ex de,hl
  1183.         get_array_value l,s3minfo.choffset
  1184.         ex de,hl
  1185.         ld d,0
  1186.         ld ix,s3mplayer.stepdatabuffer
  1187.         add ix,de
  1188.         bit 5,b
  1189.         jr z,.skipnoteinstrument
  1190.         memory_stream_read_byte a
  1191.         cp 254
  1192.         jr nc,.specialnote
  1193.         ld c,a
  1194.         rrca
  1195.         rrca
  1196.         and 0x3c
  1197.         neg
  1198.         add a,c
  1199. .specialnote
  1200.         ld (ix+S3MSTEPDATA.note),a
  1201.         memory_stream_read_byte a
  1202.         ld (ix+S3MSTEPDATA.instrument),a
  1203. .skipnoteinstrument
  1204.         bit 6,b
  1205.         jr z,.skipvolume
  1206.         memory_stream_read_byte a
  1207.         ld (ix+S3MSTEPDATA.volume),a
  1208. .skipvolume
  1209.         bit 7,b
  1210.         jr z,.skipeffect
  1211.         memory_stream_read_byte a
  1212.         memory_stream_read_byte c
  1213.         ld (ix+S3MSTEPDATA.effectcommand),a
  1214.         cp 19
  1215.         ld a,c
  1216.         jr nz,.notextended
  1217.         srl c
  1218.         srl c
  1219.         srl c
  1220.         srl c
  1221.         ld (ix+S3MSTEPDATA.extendedcommand),c
  1222.         and 15
  1223. .notextended
  1224.         ld (ix+S3MSTEPDATA.effectdata),a
  1225. .skipeffect
  1226.         memory_stream_read_byte a
  1227.         or a
  1228.         jr nz,.loop
  1229. .skiploop
  1230.         ld (memorystreamcurrentaddr),hl
  1231.         ret
  1232.  
  1233. s3mnotetoperiod
  1234. ;a = note
  1235. ;out: hl = period
  1236.         add a,a
  1237.         get_array_value a,st3periods
  1238.         inc hl
  1239.         ld h,(hl)
  1240.         ld l,a
  1241.         ret
  1242.  
  1243. s3mtuneperiod
  1244. ;iy = channel data
  1245. ;hl = period
  1246. ;out: hl = period
  1247.         ld bc,(iy+S3MCHANNEL.tunefactorcoeffoverride)
  1248.         ld a,(iy+S3MCHANNEL.tunefactorshiftoverride)
  1249.         or a
  1250.         jp p,.hasoverride
  1251.         ld bc,(iy+S3MCHANNEL.tunefactorcoeff)
  1252.         ld a,(iy+S3MCHANNEL.tunefactorshift)
  1253.         or a
  1254. .hasoverride
  1255.         push af
  1256.         ex de,hl
  1257.         call uintmul16
  1258.         pop af
  1259.         jr z,.noshift
  1260.         ld b,a
  1261. .shiftloop
  1262.         add hl,hl
  1263.         rl de
  1264.         djnz .shiftloop
  1265. .noshift
  1266.         ld hl,-S3MMINPERIOD
  1267.         add hl,de
  1268.         ex de,hl
  1269.         ret c
  1270.         ld hl,S3MMINPERIOD
  1271.         ret
  1272.  
  1273. s3mgettunefactor
  1274. ;hl = c2spd
  1275. ;output: hl = factor, a = shift
  1276. ;poor man's floating point: period*8363/c2spd = (period*factor)>>(16-shift)
  1277.         ld de,0
  1278.         exx
  1279.         ld de,8363
  1280.         ld hl,0
  1281.         call uintdiv32
  1282.         ld a,d
  1283.         or e
  1284.         ret z
  1285.         ex de,hl
  1286.         push hl
  1287.         ld b,16
  1288.         add hl,hl
  1289.         dec b
  1290.         jr nc,$-2
  1291.         ld a,h
  1292.         or l
  1293.         jr z,$+3
  1294.         inc b
  1295.         inc b
  1296.         pop hl
  1297.         ex de,hl
  1298.         ld a,b
  1299. .shiftloop
  1300.         srl de
  1301.         rr hl
  1302.         djnz .shiftloop
  1303.         ret
  1304.  
  1305. s3msetbpm equ modsetbpm
  1306. s3mwaittimer equ modwaittimer
  1307.  
  1308. s3msetfrequency
  1309. ;iy = channel data
  1310. ;hl = period
  1311.         ld a,(modperiodlookuppage)
  1312.         SETPGC000
  1313.         ld a,h
  1314.         sub 0x10
  1315.         jp c,.firsthalf
  1316. ;(hl-4096)/8+4096
  1317.         srl a : rr l
  1318.         srl a : rr l
  1319.         srl a : rr l
  1320.         add a,0x10
  1321.         ld h,a
  1322. .firsthalf
  1323.         add hl,hl
  1324. ;two-bytes lookup
  1325.         ld de,S3MHEADERADDR-2
  1326.         add hl,de
  1327.         ld d,(hl)
  1328.         inc hl
  1329.         ld l,(hl)
  1330.         ld a,(memorystreampages)
  1331.         SETPGC000
  1332.         ld a,(iy+S3MCHANNEL.index)
  1333.         add a,0x38
  1334.         ld e,a
  1335.         call opl4writewave
  1336.         ld d,l
  1337.         ld a,e
  1338.         sub 0x18
  1339.         ld e,a
  1340.         jp opl4writewave
  1341.  
  1342. s3mportaup
  1343. ;iy = channel data
  1344.         ld a,(iy+S3MCHANNEL.portaspeed)
  1345.         cp 0xe0
  1346.         ret nc
  1347.         rlca
  1348.         rlca
  1349.         ld d,a
  1350.         and 0xfc
  1351.         ld e,a
  1352.         xor d
  1353.         ld d,a
  1354. .slide  ld hl,(iy+S3MCHANNEL.period)
  1355.         sub hl,de
  1356.         jr c,.clamp
  1357.         ex de,hl
  1358.         ld hl,-S3MMINPERIOD
  1359.         add hl,de
  1360.         ex de,hl
  1361.         jr c,$+5
  1362. .clamp  ld hl,S3MMINPERIOD
  1363.         ld (iy+S3MCHANNEL.period),hl
  1364.         jp s3msetfrequency
  1365.  
  1366. s3mportadown
  1367. ;iy = channel data
  1368.         ld a,(iy+S3MCHANNEL.portaspeed)
  1369.         cp 0xe0
  1370.         ret nc
  1371.         rlca
  1372.         rlca
  1373.         ld d,a
  1374.         and 0xfc
  1375.         ld e,a
  1376.         xor d
  1377.         ld d,a
  1378. .slide  ld hl,(iy+S3MCHANNEL.period)
  1379.         add hl,de
  1380.         ex de,hl
  1381.         ld hl,-36000
  1382.         add hl,de
  1383.         ex de,hl
  1384.         jr nc,$+5
  1385.         ld hl,35999
  1386.         ld (iy+S3MCHANNEL.period),hl
  1387.         jp s3msetfrequency
  1388.  
  1389. s3mtoneporta
  1390. ;iy = channel data
  1391.         ld hl,(iy+S3MCHANNEL.period)
  1392.         ld bc,(iy+S3MCHANNEL.tempcommand)
  1393.         ld de,hl
  1394.         sub hl,bc
  1395.         ex de,hl
  1396.         jp z,s3msetfrequency ;period == tempcommand
  1397.         ld e,(iy+S3MCHANNEL.portaspeed)
  1398.         ld d,0
  1399.         jr c,.pospitchbend ;period < tempcommand
  1400.         sla de
  1401.         sla de
  1402.         sbc hl,de
  1403.         jr nc,$+5
  1404.         ld hl,0
  1405.         ld de,hl
  1406.         sub hl,bc
  1407.         ex de,hl
  1408.         jr nc,.finalize ;period >= tempcommand
  1409.         ld hl,bc
  1410.         jr .finalize
  1411. .pospitchbend
  1412.         sla de
  1413.         sla de
  1414.         add hl,de
  1415.         ld de,hl
  1416.         sub hl,bc
  1417.         ex de,hl
  1418.         jr c,$+4 ;period < tempcommand
  1419.         ld hl,bc
  1420. .finalize
  1421.         ld (iy+S3MCHANNEL.period),hl
  1422.         jp s3msetfrequency
  1423.  
  1424.         macro s3m_vibrato is_fine
  1425.         ld a,(iy+S3MCHANNEL.vibratotableposition)
  1426.         rrca
  1427.         rrca
  1428.         and 0x1f
  1429.         get_array_value e,modvibratotable
  1430.         ld hl,0
  1431.         ld a,(iy+S3MCHANNEL.vibratocommand)
  1432.         and 15
  1433.         call nz,mul8x8
  1434.         xor a
  1435.         add hl,hl : rla
  1436.         if !is_fine
  1437.         add hl,hl : rla
  1438.         add hl,hl : rla
  1439.         endif
  1440.         ld e,h
  1441.         ld d,a
  1442.         ld hl,(iy+S3MCHANNEL.period)
  1443.         bit 7,(iy+S3MCHANNEL.vibratotableposition)
  1444.         jr z,.periodup
  1445.         sub hl,de
  1446.         jr .finalize
  1447. .periodup
  1448.         add hl,de
  1449. .finalize
  1450.         ld (iy+S3MCHANNEL.tempcommand),hl
  1451.         call s3msetfrequency
  1452.         ld a,(iy+S3MCHANNEL.vibratocommand)
  1453.         rrca
  1454.         rrca
  1455.         and 0x3c
  1456.         add a,(iy+S3MCHANNEL.vibratotableposition)
  1457.         ld (iy+S3MCHANNEL.vibratotableposition),a
  1458.         endm
  1459.  
  1460. s3mfvibrato
  1461. ;iy = channel data
  1462.         s3m_vibrato 1
  1463.         ret
  1464.  
  1465. s3mvibrato
  1466. ;iy = channel data
  1467.         s3m_vibrato 0
  1468.         ret
  1469.  
  1470. s3msetvolume
  1471. ;iy = channel data
  1472. ;a = volume
  1473.         get_array_value d,modvolumetable
  1474.         sll d
  1475.         ld a,(iy+S3MCHANNEL.index)
  1476.         add a,0x50
  1477.         ld e,a
  1478.         jp opl4writewave
  1479.  
  1480. s3msetsamplenumber
  1481. ;iy = channel data
  1482. ;a = sample number
  1483.         add a,0x7f
  1484.         ld d,a
  1485.         ld a,(iy+S3MCHANNEL.index)
  1486.         add a,0x08
  1487.         ld e,a
  1488.         call opl4writewave
  1489. ;wait for the header to load
  1490.         in a,(MOON_STAT)
  1491.         and 3
  1492.         jr nz,$-4
  1493.         ret
  1494.  
  1495. s3mflushpankeyon
  1496. ;iy = channel data
  1497.         ld d,(iy+S3MCHANNEL.pankeyon)
  1498.         ld a,(iy+S3MCHANNEL.index)
  1499.         add a,0x68
  1500.         ld e,a
  1501.         jp opl4writewave
  1502.  
  1503. s3mvolumeslideT0
  1504. ;ix = step data
  1505. ;iy = channel data
  1506.         ld a,(ix+S3MSTEPDATA.effectdata)
  1507.         or a
  1508.         jr z,$+5
  1509.         ld (iy+S3MCHANNEL.volumeslide),a
  1510.         ld a,(iy+S3MCHANNEL.volumeslide)
  1511.         cp 0xf0
  1512.         jr c,.checkslideup
  1513.         and 15
  1514.         ld b,a
  1515.         ld a,(iy+S3MCHANNEL.volume)
  1516.         sub b
  1517.         jr nc,$+3
  1518.         xor a
  1519.         ld (iy+S3MCHANNEL.volume),a
  1520.         jp s3msetvolume
  1521. .checkslideup
  1522.         rrca
  1523.         rrca
  1524.         rrca
  1525.         rrca
  1526.         cp 0xf0
  1527.         ret c
  1528.         and 15
  1529.         add a,(iy+S3MCHANNEL.volume)
  1530.         clamp_volume_in_a
  1531.         ld (iy+S3MCHANNEL.volume),a
  1532.         jp s3msetvolume
  1533.  
  1534. s3mvolumeslideTN
  1535. ;iy = channel data
  1536.         ld a,(iy+S3MCHANNEL.volumeslide)
  1537.         cp 0x10
  1538.         jr nc,.checkslideup
  1539.         ld b,a
  1540.         ld a,(iy+S3MCHANNEL.volume)
  1541.         sub b
  1542.         jr nc,$+3
  1543.         xor a
  1544.         ld (iy+S3MCHANNEL.volume),a
  1545.         jp s3msetvolume
  1546. .checkslideup
  1547.         rrca
  1548.         rrca
  1549.         rrca
  1550.         rrca
  1551.         cp 0x10
  1552.         ret nc
  1553.         add a,(iy+S3MCHANNEL.volume)
  1554.         clamp_volume_in_a
  1555.         ld (iy+S3MCHANNEL.volume),a
  1556.         jp s3msetvolume
  1557.  
  1558. s3msetpanning
  1559. ;iy = channel data
  1560. ;a = panning
  1561.         get_array_value l,modpantable
  1562.         ld a,(iy+S3MCHANNEL.pankeyon)
  1563.         and 0x80
  1564.         or l
  1565.         ld (iy+S3MCHANNEL.pankeyon),a
  1566.         ret
  1567.  
  1568. s3mtremolo
  1569. ;iy = channel data
  1570.         ld a,(iy+S3MCHANNEL.tremolotableposition)
  1571.         rrca
  1572.         rrca
  1573.         and 0x1f
  1574.         get_array_value e,modvibratotable
  1575.         ld hl,0
  1576.         ld a,(iy+S3MCHANNEL.tremolocommand)
  1577.         and 15
  1578.         call nz,mul8x8
  1579.         ld a,h
  1580.         sla l
  1581.         rla
  1582.         sla l
  1583.         rla
  1584.         bit 7,(iy+S3MCHANNEL.tremolotableposition)
  1585.         jr z,.volumeup
  1586.         ld b,a
  1587.         ld a,(iy+S3MCHANNEL.volume)
  1588.         sub b
  1589.         jr nc,.finalize
  1590.         xor a
  1591.         jr .finalize
  1592. .volumeup
  1593.         add a,(iy+S3MCHANNEL.volume)
  1594.         clamp_volume_in_a
  1595. .finalize
  1596.         call s3msetvolume
  1597.         ld a,(iy+S3MCHANNEL.tremolocommand)
  1598.         rrca
  1599.         rrca
  1600.         and 0x3c
  1601.         add a,(iy+S3MCHANNEL.tremolotableposition)
  1602.         ld (iy+S3MCHANNEL.tremolotableposition),a
  1603.         ret
  1604.  
  1605. s3mfinetunefactors
  1606.         db 0x87,0x96,0x01 ;7895
  1607.         db 0x86,0xcd,0x01 ;7941
  1608.         db 0x86,0x0f,0x01 ;7985
  1609.         db 0x85,0x0b,0x01 ;8046
  1610.         db 0x84,0x0a,0x01 ;8107
  1611.         db 0x83,0x0a,0x01 ;8169
  1612.         db 0x82,0x09,0x01 ;8232
  1613.         db 0x81,0x48,0x01 ;8280
  1614.         db 0x80,0x00,0x01 ;8363 (No finetune)
  1615.         db 0xfe,0x7a,0x00 ;8413
  1616.         db 0xfc,0xf9,0x00 ;8463
  1617.         db 0xfb,0x04,0x00 ;8529
  1618.         db 0xf9,0x7f,0x00 ;8581
  1619.         db 0xf7,0x7a,0x00 ;8651
  1620.         db 0xf5,0x6f,0x00 ;8723
  1621.         db 0xf4,0x7b,0x00 ;8757
  1622.  
  1623. st3periods
  1624.         ;    C     C#    D     D#    E     F     F#    G     G#    A     A#    B
  1625.         dw 27392,25856,24384,23040,21696,20480,19328,18240,17216,16256,15360,14496 ;0
  1626.         dw 13696,12928,12192,11520,10848,10240, 9664, 9120, 8608, 8128, 7680, 7248 ;1
  1627.         dw  6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624 ;2
  1628.         dw  3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812 ;3
  1629.         dw  1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016,  960,  906 ;4
  1630.         dw   856,  808,  762,  720,  678,  640,  604,  570,  538,  508,  480,  453 ;5
  1631.         dw   428,  404,  381,  360,  339,  320,  302,  285,  269,  254,  240,  226 ;6
  1632.         dw   214,  202,  190,  180,  170,  160,  151,  143,  135,  127,  120,  113 ;7
  1633.         dw   107,  101,   95,   90,   85,   80,   75,   71,   67,   63,   60,   56 ;8
  1634. st3periodtablesize=($-st3periods)/2
  1635.