?login_element?

Subversion Repositories NedoOS

Rev

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