?login_element?

Subversion Repositories NedoOS

Rev

Rev 1870 | 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. commandE5 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. commandEE 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.         xor a
  94.         call memorystreamloadfile
  95.         ret nz
  96. ;map header to MODHEADERADDR
  97.         ld a,(memorystreampages)
  98.         SETPGC000
  99.         call modparsetype
  100.         jp nz,memorystreamfree ;sets zf=0
  101.         ld (modinfo.channelcount),a
  102.         ld a,(modheader.songlength)
  103.         ld (modinfo.songlength),a
  104.         call opl4init
  105.         call modloadpatterns
  106.         jp nz,memorystreamfree ;sets zf=0
  107.         call modloadsamples
  108.         jp nz,memorystreamfree ;sets zf=0
  109. ;init player state
  110.         ld hl,modplayer
  111.         ld de,modplayer+1
  112.         ld bc,MODPLAYER-1
  113.         ld (hl),0
  114.         ldir
  115. ;init channels
  116.         ld iy,modplayer.channels
  117.         ld bc,MODMAXCHANNELS*256
  118. .initchloop
  119.         ld (iy+MODCHANNEL.index),c
  120.         ld hl,508
  121.         ld (iy+MODCHANNEL.period),hl
  122.         ld (iy+MODCHANNEL.notenumberperiod),hl
  123.         ld a,c
  124.         and 3
  125.         get_array_value a,moddefaultpanning
  126.         call modsetpanning
  127.         ld de,MODCHANNEL
  128.         add iy,de
  129.         inc c
  130.         djnz .initchloop
  131. ;init globals
  132.         ld a,125
  133.         call modsetbpm
  134.         xor a
  135.         call modsetnextstep
  136.         ld a,6
  137.         ld (modplayer.speed),a
  138.         ld a,1
  139.         ld (modplayer.speedstep),a
  140.         xor a
  141.         ret
  142.  
  143. modparsetype
  144. ;output: a = channel count
  145.         ld hl,modtypetable
  146.         ld b,modtypecount
  147. .loop   ld a,(modheader.moduletype+0)
  148.         xor (hl)
  149.         inc hl
  150.         ld c,a
  151.         ld a,(modheader.moduletype+1)
  152.         xor (hl)
  153.         inc hl
  154.         or c
  155.         ld c,a
  156.         ld a,(modheader.moduletype+2)
  157.         xor (hl)
  158.         inc hl
  159.         or c
  160.         ld c,a
  161.         ld a,(modheader.moduletype+3)
  162.         xor (hl)
  163.         inc hl
  164.         or c
  165.         ld a,(hl)
  166.         inc hl
  167.         ret z
  168.         djnz .loop
  169.         ret
  170.  
  171. modunload
  172.         call opl4mute
  173.         jp memorystreamfree
  174.  
  175. modplay
  176.         call modwaittimer
  177.         ld a,(modplayer.speedstep)
  178.         dec a
  179.         jp nz,.tn
  180.         ld a,(modplayer.speed)
  181.         ld (modplayer.speedstep),a
  182.         ld a,(modplayer.commandEE)
  183.         or a
  184.         jr z,.noEE
  185.         dec a
  186.         ld (modplayer.commandEE),a
  187.         ret
  188. .noEE   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.commandE5),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. loadfiledata
  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,loadfiledata
  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 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.commandE5),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.commandEE),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 periodstablesize
  822.         jr c,$+4
  823.         ld a,periodstablesize-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.         ld l,a
  943.         ld h,0
  944.         add hl,hl
  945.         ld de,hl
  946.         add hl,hl
  947.         add hl,hl
  948.         add hl,hl
  949.         add hl,hl
  950.         sbc hl,de ; samplenumber*MODSAMPLEINFO
  951.         ld de,modheader.samples+MODSAMPLEINFO.volume
  952.         add hl,de
  953.         ld a,(hl)
  954.         ld (iy+MODCHANNEL.volume),a
  955.         ld de,MODSAMPLEINFO.finetune-MODSAMPLEINFO.volume
  956.         add hl,de
  957.         ld a,(hl)
  958.         ld (iy+MODCHANNEL.samplefinetune),a
  959.         ld (iy+MODCHANNEL.vibratotableposition),0
  960.         ret
  961.  
  962. modnewnote
  963. ;ix = step data
  964. ;iy = channel data
  965.         ld a,(iy+MODCHANNEL.samplenumber)
  966.         or a
  967.         ret z
  968.         ld a,(ix+MODSTEPDATA.effectcommand)
  969.         cp 0x03
  970.         jr z,.effect3
  971.         cp 0x05
  972.         jr z,.effect5
  973.         ld a,(ix+MODSTEPDATA.period)
  974.         or (ix+MODSTEPDATA.period+1)
  975.         ret z
  976.         xor a
  977.         call modsetvolume
  978.         res 7,(iy+MODCHANNEL.pankeyon)
  979.         call modflushpankeyon
  980.         ld hl,(ix+MODSTEPDATA.period)
  981.         ld (iy+MODCHANNEL.notenumberperiod),hl
  982.         call modtuneperiod
  983.         ld (iy+MODCHANNEL.period),hl
  984.         call modsetfrequency
  985.         ld a,(iy+MODCHANNEL.samplenumber)
  986.         cp (iy+MODCHANNEL.oldsamplenumber)
  987.         ret z
  988.         ld (iy+MODCHANNEL.oldsamplenumber),a
  989.         jp modsetsamplenumber
  990. .effect3
  991.         ld a,(ix+MODSTEPDATA.effectdata)
  992.         or a
  993.         jr z,.effect5
  994.         ld (iy+MODCHANNEL.pitchbendspeed),a
  995. .effect5
  996.         ld hl,(ix+MODSTEPDATA.period)
  997.         ld a,h
  998.         or l
  999.         ret z
  1000.         ld (iy+MODCHANNEL.notenumberperiod),hl
  1001.         call modtuneperiod
  1002.         ld (iy+MODCHANNEL.tempcommand),hl
  1003.         ret
  1004.  
  1005. modtuneperiod
  1006. ;iy = channel data
  1007. ;hl = period
  1008. ;out: hl = period
  1009.         ld a,(iy+MODCHANNEL.commandE5)
  1010.         or a
  1011.         jr nz,$+6
  1012.         ld a,(iy+MODCHANNEL.samplefinetune)
  1013.         and 15
  1014.         ret z
  1015.         ex de,hl
  1016.         add a,a
  1017.         get_array_value c,finetunefactors
  1018.         inc hl
  1019.         ld b,(hl)
  1020.         sla de
  1021.         call uintmul16
  1022.         bit 7,h
  1023.         jr z,$+3
  1024.         inc de
  1025.         ex de,hl
  1026.         ret
  1027.  
  1028. modportaup
  1029. ;ix = step data
  1030. ;iy = channel data
  1031.         ld hl,(iy+MODCHANNEL.period)
  1032.         ld e,(ix+MODSTEPDATA.effectdata)
  1033.         ld d,0
  1034.         sub hl,de
  1035.         jr c,.clamp
  1036.         ex de,hl
  1037.         ld hl,-1
  1038.         add hl,de
  1039.         ex de,hl
  1040.         jr c,$+5
  1041. .clamp  ld hl,1
  1042.         ld (iy+MODCHANNEL.period),hl
  1043.         jp modsetfrequency
  1044.  
  1045. modportadown
  1046. ;ix = step data
  1047. ;iy = channel data
  1048.         ld hl,(iy+MODCHANNEL.period)
  1049.         ld e,(ix+MODSTEPDATA.effectdata)
  1050.         ld d,0
  1051.         add hl,de
  1052.         ex de,hl
  1053.         ld hl,-7000
  1054.         add hl,de
  1055.         ex de,hl
  1056.         jr nc,$+5
  1057.         ld hl,6999
  1058.         ld (iy+MODCHANNEL.period),hl
  1059.         jp modsetfrequency
  1060.  
  1061. modtoneporta
  1062. ;iy = channel data
  1063.         ld hl,(iy+MODCHANNEL.period)
  1064.         ld bc,(iy+MODCHANNEL.tempcommand)
  1065.         ld de,hl
  1066.         sub hl,bc
  1067.         ex de,hl
  1068.         jp z,modsetfrequency ;period == tempcommand
  1069.         ld e,(iy+MODCHANNEL.pitchbendspeed)
  1070.         ld d,0
  1071.         jr c,.pospitchbend ;period < tempcommand
  1072.         sub hl,de
  1073.         jr nc,$+5
  1074.         ld hl,0
  1075.         ld de,hl
  1076.         sub hl,bc
  1077.         ex de,hl
  1078.         jr nc,.finalize ;period >= tempcommand
  1079.         ld hl,bc
  1080.         jr .finalize
  1081. .pospitchbend
  1082.         add hl,de
  1083.         ld de,hl
  1084.         sub hl,bc
  1085.         ex de,hl
  1086.         jr c,$+4 ;period < tempcommand
  1087.         ld hl,bc
  1088. .finalize
  1089.         ld (iy+MODCHANNEL.period),hl
  1090.         jp modsetfrequency
  1091.  
  1092. mul8x8
  1093. ;a,e = factors
  1094. ;hl = 0
  1095. ;out: hl = a*e
  1096.         ld d,h
  1097.         ld b,a
  1098.         add hl,de
  1099.         djnz $-1
  1100.         ret
  1101.  
  1102. modvibrato
  1103. ;iy = channel data
  1104.         ld a,(iy+MODCHANNEL.vibratotableposition)
  1105.         rrca
  1106.         rrca
  1107.         and 0x1f
  1108.         get_array_value e,modvibratotable
  1109.         ld hl,0
  1110.         ld a,(iy+MODCHANNEL.vibratocommand)
  1111.         and 15
  1112.         call nz,mul8x8
  1113.         sla l
  1114.         rl h
  1115.         ld e,h
  1116.         ld d,0
  1117.         ld hl,(iy+MODCHANNEL.period)
  1118.         bit 7,(iy+MODCHANNEL.vibratotableposition)
  1119.         jr z,.periodup
  1120.         sub hl,de
  1121.         jr .finalize
  1122. .periodup
  1123.         add hl,de
  1124. .finalize
  1125.         ld (iy+MODCHANNEL.tempcommand),hl
  1126.         call modsetfrequency
  1127.         ld a,(iy+MODCHANNEL.vibratocommand)
  1128.         rrca
  1129.         rrca
  1130.         and 0x3c
  1131.         add a,(iy+MODCHANNEL.vibratotableposition)
  1132.         ld (iy+MODCHANNEL.vibratotableposition),a
  1133.         ret
  1134.  
  1135. modtremolo
  1136. ;iy = channel data
  1137.         ld a,(iy+MODCHANNEL.tremolotableposition)
  1138.         rrca
  1139.         rrca
  1140.         and 0x1f
  1141.         get_array_value e,modvibratotable
  1142.         ld hl,0
  1143.         ld a,(iy+MODCHANNEL.tremolocommand)
  1144.         and 15
  1145.         call nz,mul8x8
  1146.         ld a,h
  1147.         sla l
  1148.         rla
  1149.         sla l
  1150.         rla
  1151.         bit 7,(iy+MODCHANNEL.tremolotableposition)
  1152.         jr z,.volumeup
  1153.         ld b,a
  1154.         ld a,(iy+MODCHANNEL.volume)
  1155.         sub b
  1156.         jr nc,.finalize
  1157.         xor a
  1158.         jr .finalize
  1159. .volumeup
  1160.         add a,(iy+MODCHANNEL.volume)
  1161.         clamp_volume_in_a
  1162. .finalize
  1163.         call modsetvolume
  1164.         ld a,(iy+MODCHANNEL.tremolocommand)
  1165.         rrca
  1166.         rrca
  1167.         and 0x3c
  1168.         add a,(iy+MODCHANNEL.tremolotableposition)
  1169.         ld (iy+MODCHANNEL.tremolotableposition),a
  1170.         ret
  1171.  
  1172. modvolumeslide
  1173. ;ix = step data
  1174. ;iy = channel data
  1175.         ld a,(ix+MODSTEPDATA.effectdata)
  1176.         cp 0x10
  1177.         jr nc,.volumeup
  1178.         ld b,a
  1179.         ld a,(iy+MODCHANNEL.volume)
  1180.         sub b
  1181.         jr nc,.finalize
  1182.         xor a
  1183.         jr .finalize
  1184. .volumeup
  1185.         rrca
  1186.         rrca
  1187.         rrca
  1188.         rrca
  1189.         and 15
  1190.         add a,(iy+MODCHANNEL.volume)
  1191.         clamp_volume_in_a
  1192. .finalize
  1193.         ld (iy+MODCHANNEL.volume),a
  1194.         jp modsetvolume
  1195.  
  1196. modsetsamplenumber
  1197. ;iy = channel data
  1198. ;a = sample number
  1199.         add a,0x7f
  1200.         ld d,a
  1201.         ld a,(iy+MODCHANNEL.index)
  1202.         add 0x08
  1203.         ld e,a
  1204.         call opl4writewave
  1205. ;wait for the header to load
  1206.         in a,(MOON_STAT)
  1207.         and 3
  1208.         jr nz,$-4
  1209.         ret
  1210.  
  1211. modsetfrequency
  1212. ;iy = channel data
  1213. ;hl = period
  1214.         bit 4,(iy+MODCHANNEL.samplefinetune)
  1215.         jr z,$+3
  1216.         add hl,hl
  1217.         ld a,(modperiodlookuppage)
  1218.         SETPGC000
  1219.         add hl,hl
  1220.         ld de,0xc000-2
  1221.         add hl,de
  1222.         ld d,(hl)
  1223.         inc hl
  1224.         ld l,(hl)
  1225.         ld a,(memorystreampages)
  1226.         SETPGC000
  1227.         ld a,(iy+MODCHANNEL.index)
  1228.         add 0x38
  1229.         ld e,a
  1230.         call opl4writewave
  1231.         ld d,l
  1232.         ld a,e
  1233.         sub 0x18
  1234.         ld e,a
  1235.         jp opl4writewave
  1236.  
  1237. modsetvolume
  1238. ;iy = channel data
  1239. ;a = volume
  1240.         get_array_value d,modvolumetable
  1241.         sll d
  1242.         ld a,(iy+MODCHANNEL.index)
  1243.         add a,0x50
  1244.         ld e,a
  1245.         jp opl4writewave
  1246.  
  1247. modsetpanning
  1248. ;iy = channel data
  1249. ;a = panning
  1250.         get_array_value l,modpantable
  1251.         ld a,(iy+MODCHANNEL.pankeyon)
  1252.         and 0x80
  1253.         or l
  1254.         ld (iy+MODCHANNEL.pankeyon),a
  1255.         ret
  1256.  
  1257. modflushpankeyon
  1258. ;iy = channel data
  1259.         ld d,(iy+MODCHANNEL.pankeyon)
  1260.         ld a,(iy+MODCHANNEL.index)
  1261.         add a,0x68
  1262.         ld e,a
  1263.         jp opl4writewave
  1264.  
  1265. modsetbpm
  1266. ;a = bpm (>=32)
  1267.         ld b,a
  1268.         get_array_value d,modtimertable-32
  1269.         ld a,b
  1270.         cp 125
  1271.         jr nc,.timer1
  1272.         ld e,0x03
  1273.         call opl4writefm1
  1274.         ld de,0x4204
  1275.         call opl4writefm1
  1276.         ld d,0x80
  1277.         jp opl4writefm1
  1278. .timer1
  1279.         ld e,0x02
  1280.         call opl4writefm1
  1281.         ld de,0x2104
  1282.         call opl4writefm1
  1283.         ld d,0x80
  1284.         jp opl4writefm1
  1285.  
  1286. modwaittimer
  1287.         in a,(MOON_STAT)
  1288.         rla
  1289.         jr nc,modwaittimer
  1290.         ld de,0x8004
  1291.         jp opl4writefm1
  1292.  
  1293. modvolumetable
  1294.         db 0x7F,0x5C,0x52,0x4A,0x44,0x3F,0x3B,0x37,0x34,0x31
  1295.         db 0x2F,0x2D,0x2A,0x28,0x27,0x25,0x23,0x22,0x20,0x1F
  1296.         db 0x1E,0x1C,0x1B,0x1A,0x19,0x18,0x17,0x16,0x15,0x14
  1297.         db 0x13,0x12,0x12,0x11,0x10,0x0F,0x0F,0x0E,0x0D,0x0C
  1298.         db 0x0C,0x0B,0x0B,0x0A,0x09,0x09,0x08,0x08,0x07,0x06
  1299.         db 0x06,0x05,0x05,0x04,0x04,0x03,0x03,0x03,0x02,0x02
  1300.         db 0x01,0x01,0x00,0x00,0x00
  1301.  
  1302. moddefaultpanning
  1303.         db 5,10,10,5
  1304.  
  1305. modpantable
  1306.         db 9,10,11,12,13,14,15,0,0,1,2,3,4,5,6,7
  1307.  
  1308. finetunefactors
  1309.         ; 2^( -FineTune / 12 / 8 ) as 1.15 fixed point
  1310.         dw 0x8000,0x7f14,0x7e2a,0x7d41,0x7c5b,0x7b76
  1311.         dw 0x7a92,0x79b0,0x879c,0x86a2,0x85aa,0x84b4
  1312.         dw 0x83c0,0x82cd,0x81dc,0x80ed
  1313.  
  1314. modvibratotable
  1315.         db   0, 24, 49, 74, 97,120,141,161
  1316.         db 180,197,212,224,235,244,250,253
  1317.         db 255,253,250,244,235,224,212,197
  1318.         db 180,161,141,120, 97, 74, 49, 24
  1319.  
  1320. modtimertable
  1321.         ; timer2 = 256 - 1000000 * 5 / (bpm * 2 * 323)
  1322.         db 0x0f,0x16,0x1d,0x23,0x2a,0x2f,0x35,0x3a,0x3f,0x44,0x48,0x4d,0x51,0x55,0x58,0x5c,0x5f,0x63
  1323.         db 0x66,0x69,0x6c,0x6e,0x71,0x74,0x76,0x79,0x7b,0x7d,0x80,0x82,0x84,0x86,0x88,0x89,0x8b,0x8d
  1324.         db 0x8f,0x90,0x92,0x93,0x95,0x96,0x98,0x99,0x9b,0x9c,0x9d,0x9f,0xa0,0xa1,0xa2,0xa3,0xa4,0xa5
  1325.         db 0xa7,0xa8,0xa9,0xaa,0xab,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb2,0xb3,0xb4,0xb5,0xb5
  1326.         db 0xb6,0xb7,0xb7,0xb8,0xb9,0xb9,0xba,0xbb,0xbb,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbf,0xc0,0xc1
  1327.         db 0xc1,0xc2,0xc2
  1328.         ; timer1 = 256 - 1000000 * 5 / (bpm * 2 * 81)
  1329.         db 0x0a,0x0c,0x0d,0x0f,0x11,0x13,0x15,0x17,0x18,0x1a,0x1c,0x1e,0x1f,0x21,0x22,0x24,0x26,0x27
  1330.         db 0x29,0x2a,0x2c,0x2d,0x2f,0x30,0x31,0x33,0x34,0x35,0x37,0x38,0x39,0x3b,0x3c,0x3d,0x3e,0x40
  1331.         db 0x41,0x42,0x43,0x44,0x45,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53
  1332.         db 0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x61,0x62,0x63
  1333.         db 0x64,0x65,0x65,0x66,0x67,0x68,0x68,0x69,0x6a,0x6b,0x6b,0x6c,0x6d,0x6e,0x6e,0x6f,0x70,0x70
  1334.         db 0x71,0x72,0x72,0x73,0x74,0x74,0x75,0x75,0x76,0x77,0x77,0x78,0x79,0x79,0x7a,0x7a,0x7b,0x7b
  1335.         db 0x7c,0x7d,0x7d,0x7e,0x7e,0x7f,0x7f,0x80,0x80,0x81,0x81,0x82,0x83,0x83,0x84,0x84,0x85,0x85
  1336.         db 0x86,0x86,0x87,0x87,0x87
  1337.  
  1338. modtypetable
  1339.         db "M.K.",4
  1340.         db "M!K!",4
  1341.         db "FLT4",4
  1342.         db "OCTA",8
  1343.         db "2CHN",2
  1344.         db "4CHN",4
  1345.         db "6CHN",6
  1346.         db "8CHN",8
  1347.         db "10CH",10
  1348.         db "12CH",12
  1349.         db "14CH",14
  1350.         db "16CH",16
  1351.         db "18CH",18
  1352.         db "20CH",20
  1353.         db "22CH",22
  1354.         db "24CH",24
  1355. modtypecount = ($-modtypetable)/5
  1356.  
  1357. modinitperiodlookup
  1358.         ld hl,1
  1359.         ld de,0xc000
  1360. .loop   push hl
  1361.         push de
  1362.         call opl4period
  1363.         pop de
  1364.         add hl,hl
  1365.         ld a,b
  1366.         add a,a
  1367.         add a,a
  1368.         add a,a
  1369.         add a,a
  1370.         or h
  1371.         ld (de),a
  1372.         inc de
  1373.         ld a,l
  1374.         or 1
  1375.         ld (de),a
  1376.         inc de
  1377.         pop hl
  1378.         inc hl
  1379.         bit 5,h
  1380.         jr z,.loop
  1381.         ret
  1382.  
  1383. opl4period
  1384. ;hl = period
  1385. ;out: hl = f-number, b = octave
  1386.         ld de,0
  1387.         exx
  1388.         ld de,0x0144
  1389.         ld hl,0xace0
  1390.         call uintdiv32
  1391.         push de
  1392.         push hl
  1393.         ld b,-7
  1394.         exx
  1395.         pop hl
  1396.         pop de
  1397.         srl de : rr h
  1398.         srl de : rr h
  1399.         srl de : rr h
  1400.         ld a,d
  1401.         or e
  1402.         or h
  1403.         jr z,.skip
  1404. .loop   exx
  1405.         inc b
  1406.         srl de : rr hl
  1407.         exx
  1408.         srl de : rr h
  1409.         ld a,d
  1410.         or e
  1411.         or h
  1412.         jr nz,.loop
  1413. .skip   exx
  1414.         ld a,h
  1415.         and 0x3
  1416.         ld h,a
  1417.         ret
  1418.  
  1419. modfindnotenumber
  1420. ;hl = period
  1421. ;out: a = note number
  1422. ;bc = -hl - 1
  1423.         ex de,hl
  1424.         ld hl,-3
  1425.         sub hl,de
  1426.         ld bc,hl
  1427.         ld hl,ft2periods
  1428.         xor a
  1429. .loop   ld e,(hl)
  1430.         inc hl
  1431.         ld d,(hl)
  1432.         ex de,hl
  1433.         add hl,bc
  1434.         ret nc
  1435.         ex de,hl
  1436.         inc hl
  1437.         inc a
  1438.         cp periodstablesize
  1439.         jr nz,.loop
  1440.         dec a
  1441.         ret
  1442.  
  1443. ft2periods
  1444.         ;    C     C#    D     D#    E     F     F#    G     G#    A     A#    B
  1445.         dw 6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624 ;0
  1446.         dw 3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812 ;1
  1447.         dw 1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016,  960,  906 ;2
  1448.         dw  856,  808,  762,  720,  678,  640,  604,  570,  538,  508,  480,  453 ;3
  1449.         dw  428,  404,  381,  360,  339,  320,  302,  285,  269,  254,  240,  226 ;4
  1450.         dw  214,  202,  190,  180,  170,  160,  151,  143,  135,  127,  120,  113 ;5
  1451.         dw  107,  101,   95,   90,   85,   80,   75,   71,   67,   63,   60,   56 ;6
  1452.         dw   53,   50,   47,   45,   42,   40,   37,   35,   33,   31,   30,   28 ;7
  1453. periodstablesize=($-ft2periods)/2
  1454.