Subversion Repositories NedoOS

Rev

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