Subversion Repositories NedoOS

Rev

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

  1. ; Derived from RoboPlay Amiga MOD player
  2. ; Copyright 2023 RoboSoft Inc.
  3. ; https://gitlab.com/torihino/roboplay/-/blob/master/players/src/mod.c
  4.  
  5. MODSAMPLECOUNT = 31
  6. MODWAVEHEADERBUFFERSIZE = MODSAMPLECOUNT*MOONWAVEHEADERSIZE
  7. MODSAMPLEDATASTART = MOONRAMWAVETABLESIZE*MOONWAVEHEADERSIZE+MOONSOUNDROMSIZE
  8. MODPATTERNSTEPCOUNT = 64
  9. MODSTEPDATASIZE = 4
  10. MODMAXPATTERNS = 128
  11. MODMAXCHANNELS = 24
  12. MODMAXVOLUME = 64
  13. MODHEADERADDR = 0xc000
  14.  
  15.         struct MODSAMPLEINFO
  16. samplename ds 22
  17. samplelength ds 2
  18. finetune ds 1
  19. volume ds 1
  20. samplerepeatpoint ds 2
  21. samplerepeatlength ds 2
  22.         ends
  23.  
  24.         struct MODHEADER
  25. songname ds 20
  26. samples ds MODSAMPLEINFO*MODSAMPLECOUNT
  27. songlength ds 1
  28. dummy ds 1
  29. patterntable ds 128
  30. moduletype ds 4
  31.         ends
  32.  
  33.         struct MODSTEPDATA
  34. samplenumber ds 1
  35. period ds 2
  36. effectcommand ds 1
  37. effectdata ds 1
  38. extendedcommand ds 1
  39.         ends
  40.  
  41.         struct MODCHANNEL
  42. index ds 1
  43. samplenumber ds 1
  44. oldsamplenumber ds 1
  45. notenumberperiod ds 2
  46. period ds 2
  47. volume ds 1
  48. tempcommand ds 2
  49. pitchbendspeed ds 1
  50. vibratocommand ds 1
  51. vibratotableposition ds 1
  52. tremolocommand ds 1
  53. tremolotableposition ds 1
  54. wavecontrol ds 1
  55. patternloopstart ds 1
  56. patternloopcount ds 1
  57. pankeyon ds 1
  58. samplefinetune ds 1
  59. finetuneoverride ds 1
  60.         ends
  61.  
  62.         struct MODINFO
  63. patternaddrs ds MODMAXPATTERNS*4
  64. channelcount ds 1
  65. patterncount ds 1
  66. songlength ds 1
  67. moduletypestr ds 2
  68. samplesizesinkb ds 2
  69. samplecount ds 1
  70.         ends
  71.  
  72.         struct MODPLAYER
  73. patterntableindex ds 1
  74. patternstepindex ds 1
  75. arpeggio ds 1
  76. patterndelay ds 1
  77. speed ds 1
  78. speedstep ds 1
  79. channels ds MODCHANNEL*MODMAXCHANNELS
  80. stepdatabuffer ds MODSTEPDATA*MODMAXCHANNELS
  81.         ends
  82.  
  83.         macro get_array_value dest,array
  84.         add a,(array)%256
  85.         ld l,a
  86.         adc a,(array)/256
  87.         sub l
  88.         ld h,a
  89.         ld dest,(hl)
  90.         endm
  91.  
  92. modload
  93. ;de = input file name
  94. ;out: zf=1 if the file is ready for playing, zf=0 otherwise
  95.         ld (modloadsamples.filename),de
  96.         call memorystreamloadfile
  97.         ret nz
  98. ;map header to MODHEADERADDR
  99.         ld a,(memorystreampages)
  100.         SETPGC000
  101.         call modparsetype
  102.         jp nz,memorystreamfree ;sets zf=0
  103.         ld (modinfo.moduletypestr),de
  104.         ld (modinfo.channelcount),a
  105.         ld a,(modheader.songlength)
  106.         ld (modinfo.songlength),a
  107.         call opl4init
  108.         call modloadpatterns
  109.         jp nz,memorystreamfree ;sets zf=0
  110.         call modloadsamples
  111.         jp nz,memorystreamfree ;sets zf=0
  112. ;init player state
  113.         ld hl,modplayer
  114.         ld de,modplayer+1
  115.         ld bc,MODPLAYER-1
  116.         ld (hl),0
  117.         ldir
  118. ;init channels
  119.         ld iy,modplayer.channels
  120.         ld bc,MODMAXCHANNELS*256
  121. .initchloop
  122.         ld (iy+MODCHANNEL.index),c
  123.         ld hl,508
  124.         ld (iy+MODCHANNEL.period),hl
  125.         ld (iy+MODCHANNEL.notenumberperiod),hl
  126.         ld a,c
  127.         and 3
  128.         get_array_value a,moddefaultpanning
  129.         call modsetpanning
  130.         ld de,MODCHANNEL
  131.         add iy,de
  132.         inc c
  133.         djnz .initchloop
  134. ;init globals
  135.         ld a,125
  136.         call modsetbpm
  137.         xor a
  138.         call modsetnextstep
  139.         ld a,6
  140.         ld (modplayer.speed),a
  141.         ld a,1
  142.         ld (modplayer.speedstep),a
  143.         xor a
  144.         ret
  145.  
  146. modparsetype
  147. ;output: zf=1 if successful, a = channel count, de = module type string
  148.         ld hl,modtypetable
  149.         ld b,modtypecount
  150. .loop   ld a,(modheader.moduletype+0)
  151.         xor (hl)
  152.         inc hl
  153.         ld c,a
  154.         ld a,(modheader.moduletype+1)
  155.         xor (hl)
  156.         inc hl
  157.         or c
  158.         ld c,a
  159.         ld a,(modheader.moduletype+2)
  160.         xor (hl)
  161.         inc hl
  162.         or c
  163.         ld c,a
  164.         ld a,(modheader.moduletype+3)
  165.         xor (hl)
  166.         inc hl
  167.         or c
  168.         ld a,(hl)
  169.         inc hl
  170.         ld e,(hl)
  171.         inc hl
  172.         ld d,(hl)
  173.         inc hl
  174.         ret z
  175.         djnz .loop
  176.         ld hl,modbadsignatureerrorstr
  177.         ld (ERRORSTRINGADDR),hl
  178.         ret
  179.  
  180. modbadsignatureerrorstr
  181.         db "Unsupported module type!",0
  182.  
  183. modunload
  184.         call opl4mute
  185.         jp memorystreamfree
  186.  
  187. modplay
  188.         call modwaittimer
  189.         ld a,(modplayer.speedstep)
  190.         dec a
  191.         jp nz,.tn
  192.         ld a,(modplayer.speed)
  193.         ld (modplayer.speedstep),a
  194.         ld a,(modplayer.patterndelay)
  195.         or a
  196.         jr z,.nopatterndelay
  197.         dec a
  198.         ld (modplayer.patterndelay),a
  199.         ret
  200. .nopatterndelay
  201.         call modreadstepdata
  202.         ld ix,modplayer.stepdatabuffer
  203.         ld iy,modplayer.channels
  204.         ld a,(modinfo.channelcount)
  205. .t0loop
  206.         push af
  207.         ld (iy+MODCHANNEL.finetuneoverride),0
  208.         call modsetsample
  209.         call modhandlecommandT0
  210.         ld d,(ix+MODSTEPDATA.effectcommand)
  211.         ld e,(ix+MODSTEPDATA.extendedcommand)
  212.         ld hl,0x0e0d
  213.         sub hl,de
  214.         jr z,.finalizenote
  215.         call modnewnote
  216.         ld a,(ix+MODSTEPDATA.effectcommand)
  217.         cp 0x0e
  218.         jr nz,.finalizenote
  219.         ld a,(ix+MODSTEPDATA.extendedcommand)
  220.         dec a
  221.         call z,modportaup
  222.         ld a,(ix+MODSTEPDATA.extendedcommand)
  223.         cp 2
  224.         call z,modportadown
  225. .finalizenote
  226.         ld a,(iy+MODCHANNEL.volume)
  227.         call modsetvolume
  228.         set 7,(iy+MODCHANNEL.pankeyon)
  229.         call modflushpankeyon
  230.         ld de,MODSTEPDATA
  231.         add ix,de
  232.         ld e,MODCHANNEL
  233.         add iy,de
  234.         pop af
  235.         dec a
  236.         jp nz,.t0loop
  237.         ret
  238. .tn     ld (modplayer.speedstep),a
  239.         ld a,(modplayer.arpeggio)
  240.         inc a
  241.         cp 3
  242.         jr c,$+3
  243.         xor a
  244.         ld (modplayer.arpeggio),a
  245.         ld ix,modplayer.stepdatabuffer
  246.         ld iy,modplayer.channels
  247.         ld a,(modinfo.channelcount)
  248.         ld b,a
  249. .tnloop
  250.         push bc
  251.         call modhandlecommandTN
  252.         ld de,MODSTEPDATA
  253.         add ix,de
  254.         ld e,MODCHANNEL
  255.         add iy,de
  256.         pop bc
  257.         djnz .tnloop
  258.         ret
  259.  
  260. modloadfiledata
  261.         push af,bc,de
  262.         exx
  263.         ex af,af'
  264.         push af,bc,de,hl,ix,iy
  265.         ld de,0x8000
  266.         ld hl,0x4000
  267.         call readstream_file
  268.         pop iy,ix,hl,de,bc,af
  269.         exx
  270.         ex af,af'
  271.         pop de,bc,af
  272.         ld hl,0x8000
  273.         ret
  274.  
  275. setupsampleloadingprogress
  276. ;count non-zero samples for accurate progress bar
  277.         ld bc,MODSAMPLECOUNT*256
  278.         ld de,MODSAMPLEINFO
  279.         ld ix,modheader.samples
  280. .samplecountloop
  281.         ld a,(ix+MODSAMPLEINFO.samplelength)
  282.         or (ix+MODSAMPLEINFO.samplelength+1)
  283.         jr z,$+3
  284.         inc c
  285.         add ix,de
  286.         djnz .samplecountloop
  287.         ld a,c
  288.         jp setprogressdelta
  289.  
  290. modloadsamples
  291. ;input: memory stream at samples data
  292. ;output: memory stream position is unchanged, zf=1 if samples are loaded, zf=0 otherwise
  293.         ld (.savedsp),sp
  294. .filename=$+1
  295.         ld de,0
  296.         call openstream_file
  297.         or a
  298.         ret nz
  299.         call setupsampleloadingprogress
  300.         call memorystreamgetpos
  301.         ld a,(filehandle)
  302.         ld b,a
  303.         OS_SEEKHANDLE
  304.         ld a,(modfilebufferpage)
  305.         SETPG8000
  306.         ld hl,0xffff
  307.         ld (.filebufferaddr),hl
  308. ;read samples data from file
  309.         ld hl,MODSAMPLEDATASTART%65536
  310.         ld a,MODSAMPLEDATASTART/65536
  311.         ld (.sampleaddresslo),hl
  312.         ld (.sampleaddresshi),a
  313.         ld ix,modheader.samples
  314.         ld iy,modwaveheaderbuffer
  315.         ld bc,MODSAMPLECOUNT*256
  316. .mainloop
  317.         push bc
  318.         ld h,(ix+MODSAMPLEINFO.samplelength+0)
  319.         ld l,(ix+MODSAMPLEINFO.samplelength+1)
  320.         bit 7,h
  321.         jr z,.lessthan64k
  322. ;set the bit indicating that sample's data is halved to fit 64KB OPL4 limit
  323.         set 4,(ix+MODSAMPLEINFO.finetune)
  324. .lessthan64k
  325.         jr nz,$+3
  326.         add hl,hl
  327.         ld (.samplelength),hl
  328.         ld h,(ix+MODSAMPLEINFO.samplerepeatpoint+0)
  329.         ld l,(ix+MODSAMPLEINFO.samplerepeatpoint+1)
  330.         jr nz,$+3
  331.         add hl,hl
  332.         ld bc,hl
  333.         ld h,(ix+MODSAMPLEINFO.samplerepeatlength+0)
  334.         ld l,(ix+MODSAMPLEINFO.samplerepeatlength+1)
  335.         jr nz,$+3
  336.         add hl,hl
  337.         ex de,hl
  338.         ld hl,-5
  339.         add hl,de
  340.         sbc a,a
  341.         ex de,hl
  342.         add hl,bc
  343.         dec hl
  344.         ex de,hl
  345.         or b
  346.         or c
  347.         jr nz,.hasvalidloop
  348.         ld de,(.samplelength)
  349. ;End-address and loop-address must be at least one data sample apart.
  350. ;I'm duplicating the last sample in order to stay within initialized data bounds.
  351.         ld bc,de
  352.         dec bc
  353. .hasvalidloop
  354.         ld hl,0xffff
  355.         sub hl,de
  356.         ld (iy+ 3),b ;loop hi
  357.         ld (iy+ 4),c ;loop lo
  358.         ld (iy+ 5),h ;end hi
  359.         ld (iy+ 6),l ;end lo
  360.         ld (iy+ 7),0x00 ;LFO, VIB
  361.         ld (iy+ 8),0xf0 ;AR, D1R
  362.         ld (iy+ 9),0xff ;DL, D2R
  363.         ld (iy+10),0x0f ;rate correction, RR
  364.         ld (iy+11),0x00 ;AM
  365. .sampleaddresslo=$+1
  366.         ld hl,0
  367. .sampleaddresshi=$+1
  368.         ld d,0
  369.         ld (iy+0),d ;8 bits sample, addr hi
  370.         ld (iy+1),h ;addr mi
  371.         ld (iy+2),l ;addr lo
  372. .samplelength=$+1
  373.         ld bc,0
  374.         ld a,b
  375.         or c
  376.         jr z,.nextsample
  377. ;upload sample
  378.         push bc
  379.         push de
  380.         push hl
  381.         call opl4setmemoryaddress
  382.         ld de,0x1102
  383.         call opl4writewave
  384.         opl4_wait
  385.         ld a,6
  386.         out (MOON_WREG),a
  387.         ld a,c
  388.         dec bc
  389.         inc b
  390.         ld c,b
  391.         ld b,a
  392.         xor a
  393.         bit 4,(ix+MODSAMPLEINFO.finetune)
  394.         jr z,$+4
  395.         ld a,0x23 ;'inc hl' to upload every other byte
  396.         ld (.skipbyteop),a
  397. .filebufferaddr=$+1
  398.         ld hl,0
  399. .uploadloop
  400.         bit 6,h
  401.         call nz,modloadfiledata
  402.         ld d,(hl)
  403.         inc hl
  404. .skipbyteop
  405.         ds 1
  406.         opl4_wait
  407.         ld a,d
  408.         out (MOON_WDAT),a
  409.         djnz .uploadloop
  410.         dec c
  411.         jr nz,.uploadloop
  412.         ld (.filebufferaddr),hl
  413. ;duplicate the last data sample
  414.         opl4_wait
  415.         ld a,d
  416.         out (MOON_WDAT),a
  417.         ld de,0x1002
  418.         call opl4writewave
  419.         pop hl
  420.         pop de
  421.         pop bc
  422. ;set next write address
  423.         xor a
  424.         scf ;add +1 to account for duping the last data sample
  425.         adc hl,bc
  426.         ld (.sampleaddresslo),hl
  427.         adc a,d
  428.         ld (.sampleaddresshi),a
  429. ;draw progress
  430.         push ix
  431.         push iy
  432.         call drawsampleloadingprogress
  433.         jp nz,.cancelloading
  434.         pop iy
  435.         pop ix
  436.         ld a,1
  437. .nextsample
  438.         ld bc,MODSAMPLEINFO
  439.         add ix,bc
  440.         ld c,MOONWAVEHEADERSIZE
  441.         add iy,bc
  442.         pop bc
  443.         add a,c
  444.         ld c,a
  445.         exx
  446.         call updateprogress
  447.         exx
  448.         dec b
  449.         jp nz,.mainloop
  450.         ld a,c
  451.         ld (modinfo.samplecount),a
  452.         ld hl,(.sampleaddresslo)
  453.         ld a,(.sampleaddresshi)
  454.         ld bc,-(S3MSAMPLEDATASTART%65536)+1023
  455.         add hl,bc
  456.         adc a,-(S3MSAMPLEDATASTART/65536)
  457.         ld l,h
  458.         srl a : rr l
  459.         srl a : rr l
  460.         ld h,a
  461.         ld (modinfo.samplesizesinkb),hl
  462. ;switch back to memory steam
  463.         call closestream_file
  464.         ld a,(memorystreamcurrentpage)
  465.         SETPG8000
  466. ;write headers
  467.         ld ix,modwaveheaderbuffer
  468.         ld hl,MOONSOUNDROMSIZE%65536
  469.         ld d,MOONSOUNDROMSIZE/65536
  470.         ld bc,MODWAVEHEADERBUFFERSIZE
  471.         call opl4writememory
  472.         xor a
  473.         jp initprogress
  474. .cancelloading
  475. .savedsp=$+1
  476.         ld sp,0
  477.         call closestream_file
  478.         or 1
  479.         ret
  480.  
  481. modloadpatterns
  482. ;output: pattern offsets, memory stream is positioned past patterns data
  483. ;output: zf=1 if okay, zf=0 if memory stream out of bounds
  484.         ld hl,modheader.patterntable
  485.         ld b,128
  486.         xor a
  487. .maxpatternloop
  488.         cp (hl)
  489.         jr nc,$+3
  490.         ld a,(hl)
  491.         inc hl
  492.         djnz .maxpatternloop
  493.         inc a
  494.         ld (modinfo.patterncount),a
  495.         ld de,MODSTEPDATASIZE*MODPATTERNSTEPCOUNT
  496.         ld hl,0
  497.         ld a,(modinfo.channelcount)
  498.         ld b,a
  499. .patsizeloop
  500.         add hl,de
  501.         djnz .patsizeloop
  502.         ld bc,hl
  503.         ld de,0
  504.         ld hl,MODHEADER
  505.         ld ix,modinfo.patternaddrs
  506.         ld a,(modinfo.patterncount)
  507. .patdataloop
  508.         ld (ix+0),l
  509.         ld (ix+1),h
  510.         ld (ix+2),e
  511.         ld (ix+3),d
  512.         add hl,bc
  513.         jr nc,$+3
  514.         inc de
  515.         inc ix
  516.         inc ix
  517.         inc ix
  518.         inc ix
  519.         dec a
  520.         jr nz,.patdataloop
  521. ;check for out-of-bounds access
  522.         ld a,e
  523.         ld b,h
  524.         sla b
  525.         rla
  526.         sla b
  527.         rla
  528.         cp MEMORYSTREAMMAXPAGES
  529.         ccf
  530.         sbc a,a
  531.         ret nz
  532.         call memorystreamseek
  533.         xor a
  534.         ret
  535.  
  536. modgetnextpatternindex
  537. ;output: a = pattern index
  538.         ld a,(modplayer.patterntableindex)
  539.         inc a
  540.         ld hl,modheader.songlength
  541.         cp (hl)
  542.         jr c,$+3
  543.         xor a
  544.         ld (modplayer.patterntableindex),a
  545.         ret
  546.  
  547. modgetpatternpos
  548. ;a = pattern index
  549. ;out: dehl = file stream position
  550.         ld e,a
  551.         ld d,0
  552.         ld hl,modheader.patterntable
  553.         add hl,de
  554.         ld e,(hl)
  555.         ld hl,modinfo.patternaddrs
  556.         add hl,de
  557.         add hl,de
  558.         add hl,de
  559.         add hl,de
  560.         ld c,(hl)
  561.         inc hl
  562.         ld b,(hl)
  563.         inc hl
  564.         ld e,(hl)
  565.         inc hl
  566.         ld d,(hl)
  567.         ld hl,bc
  568.         ret
  569.  
  570. modsetnextstep
  571. ;a = step index
  572. ;output: memory stream at patterntableindex/patternstepindex
  573.         dec a ;save decremented patternstepindex to cancel out its increment in T0
  574.         ld (modplayer.patternstepindex),a
  575.         ld a,(modplayer.patterntableindex)
  576.         call modgetpatternpos
  577.         ld a,(modplayer.patternstepindex)
  578.         inc a
  579.         ld b,0
  580.         add a,a
  581.         rl b
  582.         add a,a
  583.         rl b
  584.         ld c,a
  585.         ld a,(modinfo.channelcount)
  586. .loop   add hl,bc
  587.         jr nc,$+3
  588.         inc de
  589.         dec a
  590.         jr nz,.loop
  591.         jp memorystreamseek
  592.  
  593. modreadstepdata
  594. ;input: memory stream
  595. ;output: filled stepdatabuffer, memory stream at next pattern step
  596.         ld a,(modplayer.patternstepindex)
  597.         inc a
  598.         cp MODPATTERNSTEPCOUNT
  599.         jr c,.nextpatternstep
  600.         call modgetnextpatternindex
  601.         call modgetpatternpos
  602.         call memorystreamseek
  603.         xor a
  604. .nextpatternstep
  605.         ld (modplayer.patternstepindex),a
  606. ;read step data
  607.         ld ix,modplayer.stepdatabuffer
  608.         ld hl,(memorystreamcurrentaddr)
  609.         ld a,(modinfo.channelcount)
  610.         ld b,a
  611. .loop   memory_stream_read_byte c
  612.         memory_stream_read_byte d
  613.         memory_stream_read_byte e
  614.         ld a,c
  615.         and 15
  616.         ld (ix+MODSTEPDATA.period+0),d
  617.         ld (ix+MODSTEPDATA.period+1),a
  618.         ld a,e
  619.         rrca
  620.         rrca
  621.         rrca
  622.         rrca
  623.         xor c
  624.         and 15
  625.         xor c
  626.         ld (ix+MODSTEPDATA.samplenumber),a
  627.         memory_stream_read_byte c
  628.         ld a,e
  629.         and 15
  630.         ld (ix+MODSTEPDATA.effectcommand),a
  631.         cp 14
  632.         ld a,c
  633.         jr nz,.notextended
  634.         srl c
  635.         srl c
  636.         srl c
  637.         srl c
  638.         ld (ix+MODSTEPDATA.extendedcommand),c
  639.         and 15
  640. .notextended
  641.         ld (ix+MODSTEPDATA.effectdata),a
  642.         ld de,MODSTEPDATA
  643.         add ix,de
  644.         djnz .loop
  645.         ld (memorystreamcurrentaddr),hl
  646.         ret
  647.  
  648.         macro clamp_volume_in_a
  649.         cp MODMAXVOLUME+1
  650.         jr c,$+4
  651.         ld a,MODMAXVOLUME
  652.         endm
  653.  
  654. modhandlecommandT0
  655. ;ix = step data
  656. ;iy = channel data
  657.         ld a,(ix+MODSTEPDATA.effectcommand)
  658.         ld b,a
  659.         add a,a
  660.         add a,b
  661.         ld (.effectcommandtable),a
  662. .effectcommandtable=$+1
  663.         jr $
  664.         jp .doeffect0 ; 0 [Arpeggio]
  665.         ret : ds 2    ; 1 [Porta Up]
  666.         ret : ds 2    ; 2 [Porta Down]
  667.         ret : ds 2    ; 3 [Porta To Note]
  668.         jp .doeffect4 ; 4 [Vibrato]
  669.         ret : ds 2    ; 5 [Porta + Volume Slide]
  670.         ret : ds 2    ; 6 [Vibrato + Volume Slide]
  671.         jp .doeffect7 ; 7 [Tremolo]
  672.         jp .doeffect8 ; 8 [Pan]
  673.         ret : ds 2    ; 9 [Sample Offset]
  674.         ret : ds 2    ; A [Volume Slide]
  675.         ret : ds 2    ; B [Jump To Pattern]
  676.         jp .doeffectC ; C [Set Volume]
  677.         ret : ds 2    ; D [Pattern Break]
  678.         jp .doeffectE ; E [Effect]
  679.         ;;;;;;;;;;;;;;; F [Set Speed]
  680.         ld a,(ix+MODSTEPDATA.effectdata)
  681.         cp 32
  682.         jp nc,modsetbpm
  683.         ld (modplayer.speed),a
  684.         ld (modplayer.speedstep),a
  685.         ret
  686. .doeffect0
  687.         xor a
  688.         ld (modplayer.arpeggio),a
  689.         ld hl,(iy+MODCHANNEL.period)
  690.         jp modsetfrequency
  691. .doeffect4
  692.         ld bc,(iy+MODCHANNEL.period)
  693.         ld (iy+MODCHANNEL.tempcommand),bc
  694.         ld b,(ix+MODSTEPDATA.effectdata)
  695.         ld c,(iy+MODCHANNEL.vibratocommand)
  696.         ld a,b
  697.         and 0xf0
  698.         jr z,.skipvibratohi
  699.         xor c
  700.         and 0xf0
  701.         xor c
  702.         ld c,a
  703. .skipvibratohi
  704.         ld a,b
  705.         and 15
  706.         jr z,.skipvibratolo
  707.         xor c
  708.         and 15
  709.         xor c
  710.         ld c,a
  711. .skipvibratolo
  712.         ld (iy+MODCHANNEL.vibratocommand),c
  713.         ret
  714. .doeffect7
  715.         ld b,(ix+MODSTEPDATA.effectdata)
  716.         ld c,(iy+MODCHANNEL.tremolocommand)
  717.         ld a,b
  718.         and 0xf0
  719.         jr z,.skiptremolohi
  720.         xor c
  721.         and 0xf0
  722.         xor c
  723.         ld c,a
  724. .skiptremolohi
  725.         ld a,b
  726.         and 15
  727.         jr z,.skiptremololo
  728.         xor c
  729.         and 15
  730.         xor c
  731.         ld c,a
  732. .skiptremololo
  733.         ld (iy+MODCHANNEL.tremolocommand),c
  734.         ret
  735. .doeffect8
  736.         ld a,(ix+MODSTEPDATA.effectdata)
  737.         rrca
  738.         rrca
  739.         rrca
  740.         rrca
  741.         and 15
  742.         jp modsetpanning
  743. .doeffectC
  744.         ld a,(ix+MODSTEPDATA.effectdata)
  745.         clamp_volume_in_a
  746.         ld (iy+MODCHANNEL.volume),a
  747.         jp modsetvolume
  748. .doeffectE
  749.         ld a,(ix+MODSTEPDATA.extendedcommand)
  750.         ld b,a
  751.         add a,a
  752.         add a,b
  753.         ld (.exteffcommandtable),a
  754. .exteffcommandtable=$+1
  755.         jr $
  756.         ret : ds 2    ; 0 [Set Filter]
  757.         ret : ds 2    ; 1 [Fine Portamento Up]
  758.         ret : ds 2    ; 2 [Fine Portamento Down]
  759.         ret : ds 2    ; 3 [Glissando Control]
  760.         jp .doexteff4 ; 4 [Set Vibrato Waveform]
  761.         jp .doexteff5 ; 5 [Set Finetune]
  762.         ret : ds 2    ; 6 [Pattern Loop]
  763.         jp .doexteff7 ; 7 [Set Tremolo WaveForm]
  764.         jp .doexteff8 ; 8 [16 position panning]
  765.         jp .doexteff9 ; 9 [Retrig Note]
  766.         jp .doexteffA ; A [Fine Volume Slide Up]
  767.         jp .doexteffB ; B [Fine Volume Slide Down]
  768.         jp .doexteffC ; C [Cut Note]
  769.         jp .doexteffD ; D [Delay Note]
  770.         jp .doexteffE ; E [Pattern Delay]
  771.         ret           ; F
  772. .doexteff4
  773.         ld a,(iy+MODCHANNEL.wavecontrol)
  774.         ld b,(ix+MODSTEPDATA.effectdata)
  775.         and 0xfc
  776.         or b
  777.         ld (iy+MODCHANNEL.wavecontrol),a
  778.         ld (iy+MODCHANNEL.vibratotableposition),0
  779.         ret
  780. .doexteff5
  781.         ld a,(ix+MODSTEPDATA.effectdata)
  782.         or 16
  783.         ld (iy+MODCHANNEL.finetuneoverride),a
  784.         ret
  785. .doexteff7
  786.         ld a,(iy+MODCHANNEL.wavecontrol)
  787.         ld b,(ix+MODSTEPDATA.effectdata)
  788.         sla b
  789.         sla b
  790.         and 0xf3
  791.         or b
  792.         ld (iy+MODCHANNEL.wavecontrol),a
  793.         ld (iy+MODCHANNEL.tremolotableposition),0
  794.         ret
  795. .doexteff8
  796.         ld a,(ix+MODSTEPDATA.effectdata)
  797.         jp modsetpanning
  798. .doexteff9
  799.         ld a,(ix+MODSTEPDATA.effectdata)
  800.         ld (iy+MODCHANNEL.tempcommand),a
  801.         ret
  802. .doexteffA
  803.         ld a,(iy+MODCHANNEL.volume)
  804.         add a,(ix+MODSTEPDATA.effectdata)
  805.         clamp_volume_in_a
  806.         ld (iy+MODCHANNEL.volume),a
  807.         jp modsetvolume
  808. .doexteffB
  809.         ld a,(iy+MODCHANNEL.volume)
  810.         sub (ix+MODSTEPDATA.effectdata)
  811.         jr nc,$+3
  812.         xor a
  813.         ld (iy+MODCHANNEL.volume),a
  814.         jp modsetvolume
  815. .doexteffC
  816.         ld a,(ix+MODSTEPDATA.effectdata)
  817.         or a
  818.         jr z,.channeloff
  819.         ld (iy+MODCHANNEL.tempcommand),a
  820.         ret
  821. .channeloff
  822.         ld (iy+MODCHANNEL.volume),a
  823.         jp modsetvolume
  824. .doexteffD
  825.         ld a,(ix+MODSTEPDATA.effectdata)
  826.         or a
  827.         ret z
  828.         ld (iy+MODCHANNEL.tempcommand),a
  829.         ret
  830. .doexteffE
  831.         ld a,(ix+MODSTEPDATA.effectdata)
  832.         ld (modplayer.patterndelay),a
  833.         ret
  834.  
  835. modhandlecommandTN
  836. ; ix = step data
  837. ; iy = channel data
  838.         ld a,(ix+MODSTEPDATA.effectcommand)
  839.         ld b,a
  840.         add a,a
  841.         add a,b
  842.         ld (.effectcommandtable),a
  843. .effectcommandtable=$+1
  844.         jr $
  845.         jp .doeffect0   ; 0 [Arpeggio]
  846.         jp modportaup   ; 1 [Porta Up]
  847.         jp modportadown ; 2 [Porta Down]
  848.         jp modtoneporta ; 3 [Porta To Note]
  849.         jp modvibrato   ; 4 [Vibrato]
  850.         jp .doeffect5   ; 5 [Porta + Volume Slide]
  851.         jp .doeffect6   ; 6 [Vibrato + Volume Slide]
  852.         jp modtremolo   ; 7 [Tremolo]
  853.         ret : ds 2      ; 8 [Pan]
  854.         ret : ds 2      ; 9 [Sample Offset]
  855.         jp modvolumeslide ; A [Volume Slide]
  856.         jp .doeffectB   ; B [Jump To Pattern]
  857.         ret : ds 2      ; C [Set Volume]
  858.         jp .doeffectD   ; D [Pattern Break]
  859.         jp .doeffectE   ; E [Effect]
  860.         ret             ; F [Set Speed]
  861. .doeffect0
  862.         ld a,(ix+MODSTEPDATA.effectdata)
  863.         or a
  864.         ret z
  865.         ld hl,(modplayer.arpeggio)
  866.         dec l
  867.         jr z,.datahi
  868.         inc l
  869.         jr nz,.datalo
  870.         ld hl,(iy+MODCHANNEL.period)
  871.         jp modsetfrequency
  872. .datahi rrca
  873.         rrca
  874.         rrca
  875.         rrca
  876. .datalo and 15
  877.         push af
  878.         ld hl,(iy+MODCHANNEL.notenumberperiod)
  879.         call modfindnotenumber
  880.         pop bc
  881.         add a,b
  882.         cp ft2periodstablesize
  883.         jr c,$+4
  884.         ld a,ft2periodstablesize-1
  885.         ld e,a
  886.         ld d,0
  887.         ld hl,ft2periods
  888.         add hl,de
  889.         add hl,de
  890.         ld a,(hl)
  891.         inc hl
  892.         ld h,(hl)
  893.         ld l,a
  894.         call modtuneperiod
  895.         jp modsetfrequency
  896. .doeffect5
  897.         call modtoneporta
  898.         jp modvolumeslide
  899. .doeffect6
  900.         call modvibrato
  901.         jp modvolumeslide
  902. .doeffectB
  903.         ld a,(modplayer.speedstep)
  904.         dec a
  905.         ret nz
  906.         ld a,(ix+MODSTEPDATA.effectdata)
  907.         ld (modplayer.patterntableindex),a
  908.         xor a
  909.         jp modsetnextstep
  910. .doeffectD
  911.         ld a,(modplayer.speedstep)
  912.         dec a
  913.         ret nz
  914.         call modgetnextpatternindex
  915.         ld a,(ix+MODSTEPDATA.effectdata)
  916.         ld b,a
  917.         and 0xf0
  918.         xor b
  919.         ld c,a
  920.         xor b
  921.         rrca
  922.         rrca
  923.         rrca
  924.         rrca
  925.         add a,a
  926.         ld b,a
  927.         add a,a
  928.         add a,a
  929.         add a,b
  930.         add a,c
  931.         jp modsetnextstep
  932. .doeffectE
  933.         ld a,(ix+MODSTEPDATA.extendedcommand)
  934.         ld b,a
  935.         add a,a
  936.         add a,b
  937.         ld (.exteffcommandtable),a
  938. .exteffcommandtable=$+1
  939.         jr $
  940.         ret : ds 2    ; 0 [Set Filter]
  941.         ret : ds 2    ; 1 [Fine Portamento Up]
  942.         ret : ds 2    ; 2 [Fine Portamento Down]
  943.         ret : ds 2    ; 3 [Glissando Control]
  944.         ret : ds 2    ; 4 [Set Vibrato Waveform]
  945.         ret : ds 2    ; 5 [Set Finetune]
  946.         jp .doexteff6 ; 6 [Pattern Loop]
  947.         ret : ds 2    ; 7 [Set Tremolo WaveForm]
  948.         ret : ds 2    ; 8 [16 position panning]
  949.         jp modnewnote ; 9 [Retrig Note]
  950.         ret : ds 2    ; A [Fine Volume Slide Up]
  951.         ret : ds 2    ; B [Fine Volume Slide Down]
  952.         jp .doexteffC ; C [Cut Note]
  953.         jp .doexteffD ; D [Delay Note]
  954.         ret : ds 2    ; E [Pattern Delay]
  955.         ret           ; F
  956. .doexteff6
  957.         ld a,(modplayer.speedstep)
  958.         dec a
  959.         ret nz
  960.         ld a,(ix+MODSTEPDATA.effectdata)
  961.         or a
  962.         jr nz,.checkloopcount
  963.         ld a,(modplayer.patternstepindex)
  964.         ld (iy+MODCHANNEL.patternloopstart),a
  965.         ret
  966. .checkloopcount
  967.         ld b,(iy+MODCHANNEL.patternloopcount)
  968.         inc b
  969.         cp b
  970.         jr c,.restartloop
  971.         ld (iy+MODCHANNEL.patternloopcount),b
  972.         ld a,(iy+MODCHANNEL.patternloopstart)
  973.         jp modsetnextstep
  974. .restartloop
  975.         ld (iy+MODCHANNEL.patternloopcount),0
  976.         ret
  977. .doexteffC
  978.         ld a,(iy+MODCHANNEL.tempcommand)
  979.         or a
  980.         ret z
  981.         dec a
  982.         ld (iy+MODCHANNEL.tempcommand),a
  983.         ret nz
  984.         ld (iy+MODCHANNEL.volume),a
  985.         jp modsetvolume
  986. .doexteffD
  987.         ld a,(iy+MODCHANNEL.tempcommand)
  988.         or a
  989.         ret z
  990.         dec a
  991.         ld (iy+MODCHANNEL.tempcommand),a
  992.         ret nz
  993.         jp modnewnote
  994.  
  995. modsetsample
  996. ;ix = step data
  997. ;iy = channel data
  998.         ld a,(ix+MODSTEPDATA.samplenumber)
  999.         or a
  1000.         ret z
  1001.         ld (iy+MODCHANNEL.samplenumber),a
  1002.         dec a
  1003.         add a,a
  1004.         ld e,a
  1005.         ld d,0
  1006.         add a,a
  1007.         add a,a
  1008.         ld l,a
  1009.         ld h,d
  1010.         add hl,hl
  1011.         add hl,hl
  1012.         sbc hl,de ;samplenumber*MODSAMPLEINFO
  1013.         ld de,modheader.samples+MODSAMPLEINFO.volume
  1014.         add hl,de
  1015.         ld a,(hl)
  1016.         ld (iy+MODCHANNEL.volume),a
  1017.         ld de,MODSAMPLEINFO.finetune-MODSAMPLEINFO.volume
  1018.         add hl,de
  1019.         ld a,(hl)
  1020.         ld (iy+MODCHANNEL.samplefinetune),a
  1021.         ld (iy+MODCHANNEL.vibratotableposition),0
  1022.         ret
  1023.  
  1024. modnewnote
  1025. ;ix = step data
  1026. ;iy = channel data
  1027.         ld a,(iy+MODCHANNEL.samplenumber)
  1028.         or a
  1029.         ret z
  1030.         ld a,(ix+MODSTEPDATA.effectcommand)
  1031.         cp 0x03
  1032.         jr z,.effect3
  1033.         cp 0x05
  1034.         jr z,.effect5
  1035.         ld a,(ix+MODSTEPDATA.period)
  1036.         or (ix+MODSTEPDATA.period+1)
  1037.         ret z
  1038.         xor a
  1039.         call modsetvolume
  1040.         res 7,(iy+MODCHANNEL.pankeyon)
  1041.         call modflushpankeyon
  1042.         ld hl,(ix+MODSTEPDATA.period)
  1043.         ld (iy+MODCHANNEL.notenumberperiod),hl
  1044.         call modtuneperiod
  1045.         ld (iy+MODCHANNEL.period),hl
  1046.         call modsetfrequency
  1047.         ld a,(iy+MODCHANNEL.samplenumber)
  1048.         cp (iy+MODCHANNEL.oldsamplenumber)
  1049.         ret z
  1050.         ld (iy+MODCHANNEL.oldsamplenumber),a
  1051.         jp modsetsamplenumber
  1052. .effect3
  1053.         ld a,(ix+MODSTEPDATA.effectdata)
  1054.         or a
  1055.         jr z,.effect5
  1056.         ld (iy+MODCHANNEL.pitchbendspeed),a
  1057. .effect5
  1058.         ld hl,(ix+MODSTEPDATA.period)
  1059.         ld a,h
  1060.         or l
  1061.         ret z
  1062.         ld (iy+MODCHANNEL.notenumberperiod),hl
  1063.         call modtuneperiod
  1064.         ld (iy+MODCHANNEL.tempcommand),hl
  1065.         ret
  1066.  
  1067. modtuneperiod
  1068. ;iy = channel data
  1069. ;hl = period
  1070. ;out: hl = period
  1071.         ld a,(iy+MODCHANNEL.finetuneoverride)
  1072.         or a
  1073.         jr nz,$+6
  1074.         ld a,(iy+MODCHANNEL.samplefinetune)
  1075.         and 15
  1076.         ret z
  1077.         ex de,hl
  1078.         add a,a
  1079.         get_array_value c,modfinetunefactors-2
  1080.         inc hl
  1081.         ld b,(hl)
  1082.         sla de
  1083.         call uintmul16
  1084.         bit 7,h
  1085.         jr z,$+3
  1086.         inc de
  1087.         ex de,hl
  1088.         ret
  1089.  
  1090. modportaup
  1091. ;ix = step data
  1092. ;iy = channel data
  1093.         ld hl,(iy+MODCHANNEL.period)
  1094.         ld e,(ix+MODSTEPDATA.effectdata)
  1095.         ld d,0
  1096.         sub hl,de
  1097.         jr c,.clamp
  1098.         jr nz,$+5
  1099. .clamp  ld hl,1
  1100.         ld (iy+MODCHANNEL.period),hl
  1101.         jp modsetfrequency
  1102.  
  1103. modportadown
  1104. ;ix = step data
  1105. ;iy = channel data
  1106.         ld hl,(iy+MODCHANNEL.period)
  1107.         ld e,(ix+MODSTEPDATA.effectdata)
  1108.         ld d,0
  1109.         add hl,de
  1110.         ex de,hl
  1111.         ld hl,-7000
  1112.         add hl,de
  1113.         ex de,hl
  1114.         jr nc,$+5
  1115.         ld hl,6999
  1116.         ld (iy+MODCHANNEL.period),hl
  1117.         jp modsetfrequency
  1118.  
  1119. modtoneporta
  1120. ;iy = channel data
  1121.         ld hl,(iy+MODCHANNEL.period)
  1122.         ld bc,(iy+MODCHANNEL.tempcommand)
  1123.         ld de,hl
  1124.         sub hl,bc
  1125.         ex de,hl
  1126.         jp z,modsetfrequency ;period == tempcommand
  1127.         ld e,(iy+MODCHANNEL.pitchbendspeed)
  1128.         ld d,0
  1129.         jr c,.pospitchbend ;period < tempcommand
  1130.         sub hl,de
  1131.         jr nc,$+5
  1132.         ld hl,0
  1133.         ld de,hl
  1134.         sub hl,bc
  1135.         ex de,hl
  1136.         jr nc,.finalize ;period >= tempcommand
  1137.         ld hl,bc
  1138.         jr .finalize
  1139. .pospitchbend
  1140.         add hl,de
  1141.         ld de,hl
  1142.         sub hl,bc
  1143.         ex de,hl
  1144.         jr c,$+4 ;period < tempcommand
  1145.         ld hl,bc
  1146. .finalize
  1147.         ld (iy+MODCHANNEL.period),hl
  1148.         jp modsetfrequency
  1149.  
  1150. mul8x8
  1151. ;a,e = factors
  1152. ;hl = 0
  1153. ;out: hl = a*e
  1154.         ld d,h
  1155.         ld b,a
  1156.         add hl,de
  1157.         djnz $-1
  1158.         ret
  1159.  
  1160. modvibrato
  1161. ;iy = channel data
  1162.         ld a,(iy+MODCHANNEL.vibratotableposition)
  1163.         rrca
  1164.         rrca
  1165.         and 0x1f
  1166.         get_array_value e,modvibratotable
  1167.         ld hl,0
  1168.         ld a,(iy+MODCHANNEL.vibratocommand)
  1169.         and 15
  1170.         call nz,mul8x8
  1171.         sla l
  1172.         rl h
  1173.         ld e,h
  1174.         ld d,0
  1175.         ld hl,(iy+MODCHANNEL.period)
  1176.         bit 7,(iy+MODCHANNEL.vibratotableposition)
  1177.         jr z,.periodup
  1178.         sub hl,de
  1179.         jr .finalize
  1180. .periodup
  1181.         add hl,de
  1182. .finalize
  1183.         ld (iy+MODCHANNEL.tempcommand),hl
  1184.         call modsetfrequency
  1185.         ld a,(iy+MODCHANNEL.vibratocommand)
  1186.         rrca
  1187.         rrca
  1188.         and 0x3c
  1189.         add a,(iy+MODCHANNEL.vibratotableposition)
  1190.         ld (iy+MODCHANNEL.vibratotableposition),a
  1191.         ret
  1192.  
  1193. modtremolo
  1194. ;iy = channel data
  1195.         ld a,(iy+MODCHANNEL.tremolotableposition)
  1196.         rrca
  1197.         rrca
  1198.         and 0x1f
  1199.         get_array_value e,modvibratotable
  1200.         ld hl,0
  1201.         ld a,(iy+MODCHANNEL.tremolocommand)
  1202.         and 15
  1203.         call nz,mul8x8
  1204.         ld a,h
  1205.         sla l
  1206.         rla
  1207.         sla l
  1208.         rla
  1209.         bit 7,(iy+MODCHANNEL.tremolotableposition)
  1210.         jr z,.volumeup
  1211.         ld b,a
  1212.         ld a,(iy+MODCHANNEL.volume)
  1213.         sub b
  1214.         jr nc,.finalize
  1215.         xor a
  1216.         jr .finalize
  1217. .volumeup
  1218.         add a,(iy+MODCHANNEL.volume)
  1219.         clamp_volume_in_a
  1220. .finalize
  1221.         call modsetvolume
  1222.         ld a,(iy+MODCHANNEL.tremolocommand)
  1223.         rrca
  1224.         rrca
  1225.         and 0x3c
  1226.         add a,(iy+MODCHANNEL.tremolotableposition)
  1227.         ld (iy+MODCHANNEL.tremolotableposition),a
  1228.         ret
  1229.  
  1230. modvolumeslide
  1231. ;ix = step data
  1232. ;iy = channel data
  1233.         ld a,(ix+MODSTEPDATA.effectdata)
  1234.         cp 0x10
  1235.         jr nc,.volumeup
  1236.         ld b,a
  1237.         ld a,(iy+MODCHANNEL.volume)
  1238.         sub b
  1239.         jr nc,.finalize
  1240.         xor a
  1241.         jr .finalize
  1242. .volumeup
  1243.         rrca
  1244.         rrca
  1245.         rrca
  1246.         rrca
  1247.         and 15
  1248.         add a,(iy+MODCHANNEL.volume)
  1249.         clamp_volume_in_a
  1250. .finalize
  1251.         ld (iy+MODCHANNEL.volume),a
  1252.         jp modsetvolume
  1253.  
  1254. modsetsamplenumber
  1255. ;iy = channel data
  1256. ;a = sample number
  1257.         add a,0x7f
  1258.         ld d,a
  1259.         ld a,(iy+MODCHANNEL.index)
  1260.         add a,0x08
  1261.         ld e,a
  1262.         call opl4writewave
  1263. ;wait for the header to load
  1264.         in a,(MOON_STAT)
  1265.         and 3
  1266.         jr nz,$-4
  1267.         ret
  1268.  
  1269. modsetfrequency
  1270. ;iy = channel data
  1271. ;hl = period
  1272.         bit 4,(iy+MODCHANNEL.samplefinetune)
  1273.         jr z,$+3
  1274.         add hl,hl
  1275.         ld a,(modperiodlookuppage)
  1276.         SETPGC000
  1277.         ld a,h
  1278.         cp 0x04
  1279.         jp c,.firsthalf
  1280. ;((hl*4-4096)/8+4096)*2 = hl+7168
  1281.         add a,0x1c
  1282.         ld h,a
  1283.         res 0,l
  1284.         jr .sampletable
  1285. .firsthalf
  1286.         add hl,hl
  1287.         add hl,hl
  1288.         add hl,hl
  1289. .sampletable
  1290.         ld de,MODHEADERADDR-2
  1291.         add hl,de
  1292.         ld d,(hl)
  1293.         inc hl
  1294.         ld l,(hl)
  1295.         ld a,(memorystreampages)
  1296.         SETPGC000
  1297.         ld a,(iy+MODCHANNEL.index)
  1298.         add a,0x38
  1299.         ld e,a
  1300.         call opl4writewave
  1301.         ld d,l
  1302.         ld a,e
  1303.         sub 0x18
  1304.         ld e,a
  1305.         jp opl4writewave
  1306.  
  1307. modsetvolume
  1308. ;iy = channel data
  1309. ;a = volume
  1310.         get_array_value d,modvolumetable
  1311.         sll d
  1312.         ld a,(iy+MODCHANNEL.index)
  1313.         add a,0x50
  1314.         ld e,a
  1315.         jp opl4writewave
  1316.  
  1317. modsetpanning
  1318. ;iy = channel data
  1319. ;a = panning
  1320.         get_array_value l,modpantable
  1321.         ld a,(iy+MODCHANNEL.pankeyon)
  1322.         and 0x80
  1323.         or l
  1324.         ld (iy+MODCHANNEL.pankeyon),a
  1325.         ret
  1326.  
  1327. modflushpankeyon
  1328. ;iy = channel data
  1329.         ld d,(iy+MODCHANNEL.pankeyon)
  1330.         ld a,(iy+MODCHANNEL.index)
  1331.         add a,0x68
  1332.         ld e,a
  1333.         jp opl4writewave
  1334.  
  1335. modsetbpm
  1336. ;a = bpm (>=32)
  1337.         ld b,a
  1338.         get_array_value d,modtimertable-32
  1339.         ld a,b
  1340.         cp 125
  1341.         jr nc,.timer1
  1342.         ld e,0x03
  1343.         call opl4writefm1
  1344.         ld de,0x4204
  1345.         call opl4writefm1
  1346.         ld d,0x80
  1347.         jp opl4writefm1
  1348. .timer1
  1349.         ld e,0x02
  1350.         call opl4writefm1
  1351.         ld de,0x2104
  1352.         call opl4writefm1
  1353.         ld d,0x80
  1354.         jp opl4writefm1
  1355.  
  1356. modwaittimer
  1357.         in a,(MOON_STAT)
  1358.         rla
  1359.         jr nc,modwaittimer
  1360.         ld de,0x8004
  1361.         jp opl4writefm1
  1362.  
  1363. modvolumetable
  1364.         db 0x7f,0x60,0x50,0x47,0x40,0x3b,0x37,0x33,0x30,0x2d
  1365.         db 0x2b,0x29,0x27,0x25,0x23,0x22,0x20,0x1f,0x1d,0x1c
  1366.         db 0x1b,0x1a,0x19,0x18,0x17,0x16,0x15,0x14,0x13,0x12
  1367.         db 0x12,0x11,0x10,0x0f,0x0f,0x0e,0x0d,0x0d,0x0c,0x0b
  1368.         db 0x0b,0x0a,0x0a,0x09,0x09,0x08,0x08,0x07,0x07,0x06
  1369.         db 0x06,0x05,0x05,0x04,0x04,0x04,0x03,0x03,0x02,0x02
  1370.         db 0x01,0x01,0x01,0x00,0x00
  1371.  
  1372. moddefaultpanning
  1373.         db 5,10,10,5
  1374.  
  1375. modpantable
  1376.         db 9,10,11,12,13,14,15,0,0,1,2,3,4,5,6,7
  1377.  
  1378. modfinetunefactors
  1379.         ; 2^( -FineTune / 12 / 8 ) as 1.15 fixed point
  1380.         dw 0x7f14,0x7e2a,0x7d41,0x7c5b,0x7b76,0x7a92,0x79b0,0x879c
  1381.         dw 0x86a2,0x85aa,0x84b4,0x83c0,0x82cd,0x81dc,0x80ed
  1382.  
  1383. modvibratotable
  1384.         db   0, 24, 49, 74, 97,120,141,161
  1385.         db 180,197,212,224,235,244,250,253
  1386.         db 255,253,250,244,235,224,212,197
  1387.         db 180,161,141,120, 97, 74, 49, 24
  1388.  
  1389. modtimertable
  1390.         ; timer2 = 256 - 1000000 * 5 / (bpm * 2 * 323)
  1391.         db 0x0f,0x16,0x1d,0x23,0x2a,0x2f,0x35,0x3a,0x3f,0x44,0x48,0x4d,0x51,0x55,0x58,0x5c,0x5f,0x63
  1392.         db 0x66,0x69,0x6c,0x6e,0x71,0x74,0x76,0x79,0x7b,0x7d,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d
  1393.         db 0x8f,0x90,0x92,0x93,0x95,0x96,0x98,0x99,0x9b,0x9c,0x9d,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5
  1394.         db 0xa7,0xa8,0xa9,0xaa,0xab,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb2,0xb3,0xb4,0xb5,0xb5
  1395.         db 0xb6,0xb7,0xb7,0xb8,0xb9,0xb9,0xba,0xbb,0xbb,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbf,0xc0,0xc1
  1396.         db 0xc1,0xc2,0xc2
  1397.         ; timer1 = 256 - 1000000 * 5 / (bpm * 2 * 81)
  1398.         db 0x0a,0x0c,0x0d,0x0f,0x11,0x13,0x15,0x17,0x18,0x1a,0x1c,0x1e,0x1f,0x21,0x22,0x24,0x26,0x27
  1399.         db 0x29,0x2a,0x2c,0x2d,0x2f,0x30,0x31,0x33,0x34,0x35,0x37,0x38,0x39,0x3b,0x3c,0x3d,0x3e,0x40
  1400.         db 0x41,0x42,0x43,0x44,0x45,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53
  1401.         db 0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x61,0x62,0x63
  1402.         db 0x64,0x65,0x65,0x66,0x67,0x68,0x68,0x69,0x6a,0x6b,0x6b,0x6c,0x6d,0x6e,0x6e,0x6f,0x70,0x70
  1403.         db 0x71,0x72,0x72,0x73,0x74,0x74,0x75,0x75,0x76,0x77,0x77,0x78,0x79,0x79,0x7a,0x7a,0x7b,0x7b
  1404.         db 0x7c,0x7d,0x7d,0x7e,0x7e,0x7f,0x7f,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85
  1405.         db 0x86,0x86,0x87,0x87,0x87
  1406.  
  1407. modtypetable
  1408.         db "M.K.", 4 : dw protrackerstr
  1409.         db "M!K!", 4 : dw protrackerstr
  1410.         db "FLT4", 4 : dw startrekkerstr
  1411.         db "OCTA", 8 : dw octalyzerstr
  1412.         db "2CHN", 2 : dw fasttrackerstr
  1413.         db "4CHN", 4 : dw fasttrackerstr
  1414.         db "6CHN", 6 : dw fasttrackerstr
  1415.         db "8CHN", 8 : dw fasttrackerstr
  1416.         db "10CH",10 : dw fasttrackerstr
  1417.         db "12CH",12 : dw fasttrackerstr
  1418.         db "14CH",14 : dw fasttrackerstr
  1419.         db "16CH",16 : dw fasttrackerstr
  1420.         db "18CH",18 : dw fasttrackerstr
  1421.         db "20CH",20 : dw fasttrackerstr
  1422.         db "22CH",22 : dw fasttrackerstr
  1423.         db "24CH",24 : dw fasttrackerstr
  1424. modtypecount = ($-modtypetable)/5
  1425.  
  1426. protrackerstr db "ProTracker",0
  1427. startrekkerstr db "StarTrekker",0
  1428. fasttrackerstr db "FastTracker",0
  1429. octalyzerstr db "Octalyzer",0
  1430.  
  1431. modfindnotenumber
  1432. ;hl = period
  1433. ;out: a = note number
  1434. ;bc = -hl - 1
  1435.         ex de,hl
  1436.         ld hl,-3
  1437.         sub hl,de
  1438.         ld bc,hl
  1439.         ld hl,ft2periods
  1440.         xor a
  1441. .loop   ld e,(hl)
  1442.         inc hl
  1443.         ld d,(hl)
  1444.         ex de,hl
  1445.         add hl,bc
  1446.         ret nc
  1447.         ex de,hl
  1448.         inc hl
  1449.         inc a
  1450.         cp ft2periodstablesize
  1451.         jr nz,.loop
  1452.         dec a
  1453.         ret
  1454.  
  1455. ft2periods
  1456.         ;    C     C#    D     D#    E     F     F#    G     G#    A     A#    B
  1457.         dw 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624 ;0
  1458.         dw 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812 ;1
  1459.         dw 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016,  960,  906 ;2
  1460.         dw  856,  808,  762,  720,  678,  640,  604,  570,  538,  508,  480,  453 ;3
  1461.         dw  428,  404,  381,  360,  339,  320,  302,  285,  269,  254,  240,  226 ;4
  1462.         dw  214,  202,  190,  180,  170,  160,  151,  143,  135,  127,  120,  113 ;5
  1463.         dw  107,  101,   95,   90,   85,   80,   75,   71,   67,   63,   60,   56 ;6
  1464.         dw   53,   50,   47,   45,   42,   40,   37,   35,   33,   31,   30,   28 ;7
  1465. ft2periodstablesize=($-ft2periods)/2
  1466.