Subversion Repositories NedoOS

Rev

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

  1. ; ProTracker modules player for AY8910/TurboSound
  2. ; Player for MIDI UART connected to AY port A.2 (e.g. ZX MultiSound)
  3.  
  4.         DEVICE ZXSPECTRUM128
  5.         include "../_sdk/sys_h.asm"
  6.         include "playerdefs.asm"
  7.  
  8. MDLADDR = 0x8000
  9. TITLELENGTH = 64
  10. MEMORYSTREAMMAXPAGES = 210
  11. MEMORYSTREAMERRORMASK = 255
  12. MIDMAXTRACKS = 64
  13. MIDCHANNELS = 16
  14.  
  15.         struct MIDTRACK
  16. lastcommand ds 1
  17. nexteventtick ds 4
  18. streamoffset ds 4
  19.         ends
  20.  
  21.         struct MIDPLAYER
  22. filetype ds 1
  23. trackcount ds 1
  24. tickcounter ds 4
  25. ticksperupdate ds 4
  26. ticksperqnoteXupdatelen ds 4
  27. tracks ds MIDTRACK*MIDMAXTRACKS
  28.         ends
  29.  
  30.         org PLAYERSTART
  31.  
  32. begin   PLAYERHEADER
  33.  
  34. isfilesupported
  35. ;cde = file extension
  36. ;out: zf=1 if this player can handle the file and the sound hardware is available, zf=0 otherwise
  37.         call ismidfile
  38.         jr nz,.checkpt
  39.         ld hl,0
  40.         ld (MUSICTITLEADDR),hl
  41.         ld hl,musicprogress+1
  42.         ld (MUSICPROGRESSADDR),hl
  43.         jp initprogress
  44. .checkpt
  45.         ld a,c
  46.         cp 'p'
  47.         ret nz
  48.         ld a,d
  49.         cp 't'
  50.         ret nz
  51.         ld a,e
  52.         cp '2'
  53.         jr nz,.checkpt3
  54. ;prepare local variables
  55.         ld hl,0
  56.         ld (MUSICTITLEADDR),hl
  57.         ld (MUSICPROGRESSADDR),hl
  58.         ret
  59. .checkpt3
  60.         cp '3'
  61.         ret nz
  62. ;prepare local variables
  63.         ld hl,0
  64.         ld (MUSICTITLEADDR),hl
  65.         ld hl,musicprogress+1
  66.         ld (MUSICPROGRESSADDR),hl
  67.         jp initprogress
  68.  
  69. ismidfile
  70. ;cde = file extension
  71. ;out: zf=1 if .mod, zf=0 otherwise
  72.         ld a,'m'
  73.         cp c
  74.         ret nz
  75.         ld a,'i'
  76.         cp d
  77.         ret nz
  78.         ld a,'d'
  79.         cp e
  80.         ret
  81.  
  82. playerinit
  83. ;hl,ix = GPSETTINGS
  84. ;a = player page
  85. ;out: zf=1 if init is successful, hl=init message
  86.         ld (playerpage),a
  87.         ld a,(hl)
  88.         ld (page8000),a
  89.         inc hl
  90.         ld a,(hl)
  91.         ld (pageC000),a
  92.         call setuartdelay
  93.         ld a,(waitspincount)
  94.         or a
  95.         call z,initwaitspincount
  96.         ld hl,initokstr
  97.         xor a
  98.         ret
  99.  
  100. setuartdelay
  101. ;ix = GPSETTINGS
  102.         ld de,(ix+GPSETTINGS.midiuartdelayoverride)
  103.         ld a,d
  104.         or e
  105.         ret z
  106.         ld bc,3*256
  107. .loop   ld a,(de)
  108.         sub '0'
  109.         jr c,.done
  110.         cp 10
  111.         jr nc,.done
  112.         ld h,a
  113.         ld a,c
  114.         add a,a
  115.         ld c,a
  116.         add a,a
  117.         add a,a
  118.         add a,c
  119.         add a,h
  120.         ld c,a
  121.         inc de
  122.         djnz .loop
  123. .done   ld a,c
  124.         ld (waitspincount),a
  125.         ret
  126.  
  127. playerdeinit
  128.         ret
  129.  
  130. musicload
  131. ;cde = file extension
  132. ;hl = input file name
  133. ;out: a = device mask, zf=1 if the file is ready for playing, zf=0 otherwise
  134.         call ismidfile
  135.         jr nz,.ptfile
  136.         call midloadfile
  137.         ret nz
  138.         ld a,255
  139.         ld (isplayingmidfile),a
  140.         ld a,DEVICE_MIDI_UART_MASK
  141.         cp a
  142.         ret
  143. .ptfile ex de,hl
  144.         call openstream_file
  145.         or a
  146.         ret nz
  147. page8000=$+1
  148.         ld a,0
  149.         SETPG8000
  150. pageC000=$+1
  151.         ld a,0
  152.         SETPGC000
  153.         ld de,MDLADDR
  154.         ld hl,de
  155.         call readstream_file
  156.         push hl
  157.         call closestream_file
  158.         pop ix
  159.         call getconfig
  160.         ld (SETUP),a
  161.         ld de,MDLADDR
  162.         add hl,de
  163.         ex hl,de
  164.         call INIT
  165. playerpage=$+1
  166.         ld a,0
  167.         ld hl,PLAY
  168.         OS_SETMUSIC
  169.         xor a
  170.         ld (isplayingmidfile),a
  171.         ld a,(is_ts)
  172.         or a
  173.         ld a,DEVICE_AY_MASK
  174.         jr z,$+4
  175.         ld a,DEVICE_TURBOSOUND_MASK
  176.         cp a
  177.         ret
  178.  
  179. musicunload
  180.         ld a,(isplayingmidfile)
  181.         or a
  182.         jp nz,midunload
  183.         ld a,(playerpage)
  184.         ld hl,play_reter
  185.         OS_SETMUSIC
  186.         jp MUTE
  187.  
  188. play_reter
  189.         ret
  190.  
  191. musicplay
  192. ;out: zf=0 if still playing, zf=1 otherwise
  193.         ld a,(isplayingmidfile)
  194.         or a
  195.         jp nz,midplay
  196.         YIELD
  197.         YIELD
  198.         YIELD
  199.         YIELD
  200.         YIELD
  201.         ld a,(SETUP)
  202.         and 2
  203.         ld a,(VARS1+VRS.CurPos)
  204.         call z,updateprogress
  205.         ld a,(SETUP)
  206.         cpl
  207.         and 128
  208.         ret
  209.  
  210. findts
  211. ;ix = file size
  212. ;out: zf = 1 if TS data is found, hl = offset to the second module if available
  213.         ld de,MDLADDR
  214.         add ix,de ;past-the-end address of the data buffer
  215.         ld a,'0'
  216.         cp (ix-4)
  217.         ret nz
  218.         ld a,'2'
  219.         cp (ix-3)
  220.         ret nz
  221.         ld a,'T'
  222.         cp (ix-2)
  223.         ret nz
  224.         ld a,'S'
  225.         cp (ix-1)
  226.         ret nz
  227.         ld hl,(ix-12)
  228.         ret
  229.  
  230. getconfig
  231. ;ix = file size
  232. ;out: a = player config bits, hl = offset to the second module if available
  233.         ld a,(MDLADDR)
  234.         cp 'V'
  235.         jr z,.ispt3
  236.         cp 'P'
  237.         jr z,.ispt3
  238.         ld a,%00000011 ;PT2
  239.         ret
  240. .ispt3
  241.         ld a,(MDLADDR+101)
  242.         call setprogressdelta
  243. ;set title
  244.         ld hl,titlestr
  245.         ld (MUSICTITLEADDR),hl
  246.         ld de,titlestr+1
  247.         ld bc,TITLELENGTH-1
  248.         ld (hl),' '
  249.         ldir
  250.         xor a
  251.         ld (de),a
  252.         ld hl,titlestr-1
  253.         ld de,MDLADDR+30
  254.         ld bc,68*256+TITLELENGTH
  255. .copytitleloop
  256.         ld a,(de)
  257.         inc de
  258.         cp ' '
  259.         jr nz,$+5
  260.         cp (hl)
  261.         jr z,$+7
  262.         inc hl
  263.         ld (hl),a
  264.         dec c
  265.         jr z,$+4
  266.         djnz .copytitleloop
  267.         call findts
  268.         ld a,%00010001 ;2xPT3
  269.         ret z
  270.         ld a,%00100001 ;PT3
  271.         ret
  272.  
  273.         include "../_sdk/file.asm"
  274.         include "ptsplay/ptsplay.asm"
  275.         include "common/memorystream.asm"
  276.         include "common/muldiv.asm"
  277.         include "progress.asm"
  278.  
  279. VSYNC_FREQ = 49
  280. BAUD_RATE = 31250
  281. WAIT_LOOP_TSTATES = 14
  282. BENCHMARK_LOOP_TSTATES = 42
  283. DEFAULT_QNOTE_DURATION_MCS = 500000
  284.  
  285. VSYNC_MCS = 1000000/VSYNC_FREQ
  286. CC1 = WAIT_LOOP_TSTATES*BAUD_RATE
  287. CC2 = (VSYNC_FREQ*BENCHMARK_LOOP_TSTATES*65536+CC1/2)/CC1
  288.  
  289. SSG_REG = 0xfffd
  290. SSG_DAT = 0xbffd
  291.  
  292.         macro wait_32us reducebytstates
  293.         ld a,(waitspincount)
  294.         add a,-(20+reducebytstates+7)/14
  295. .loop   dec a
  296.         jp z,.done
  297.         dec a
  298.         jp z,.done
  299.         dec a
  300.         jp z,.done
  301.         dec a
  302.         jp nz,.loop
  303. .done   ;that's all, folks
  304.         endm
  305.  
  306. midinitport
  307.         ld bc,SSG_REG
  308.         ld a,0xff
  309.         out (c),a
  310.         ld a,7
  311.         out (c),a
  312.         ld bc,SSG_DAT
  313.         ld a,0xfc
  314.         out (c),a
  315.         ret
  316.  
  317. midsend3
  318. ;dhl = data
  319.         call midsendbyte
  320. midsend2
  321. ;hl = data
  322.         ld d,h
  323.         call midsendbyte
  324.         ld d,l
  325. midsendbyte
  326. ;d = data
  327.         di
  328.         ld bc,SSG_REG
  329.         ld a,14
  330.         out (c),a
  331.         ld bc,SSG_DAT
  332.         ld a,%11111010
  333.         out (c),a
  334.         wait_32us 42
  335.         scf
  336.         rr d
  337. .loop   sbc a,a
  338.         and %00000100
  339.         add a,%11111010
  340.         out (c),a
  341.         wait_32us 48
  342.         srl d
  343.         jp nz,.loop
  344.         nop
  345.         nop
  346.         nop
  347.         ld a,%11111110
  348.         out (c),a
  349.         wait_32us 0
  350.         ei
  351.         ret
  352.  
  353. initwaitspincount
  354.         call swapinterrupthandler ;avoid OS while benchmarking
  355.         halt
  356.         ld hl,0
  357.         ld e,0
  358.         xor a
  359.         ld (.spincount),a
  360.         ld a,32
  361.         halt
  362. ;--> 42 t-states loop start
  363. .loop   inc e
  364.         jp nz,$+4
  365.         inc hl
  366.         nop
  367. .spincount=$+1
  368.         ld bc,0
  369.         cp c
  370.         jp nc,.loop
  371. ;<-- loop end
  372.         push de
  373.         push hl
  374.         call swapinterrupthandler ;restore OS handler
  375.         pop hl
  376.         pop de
  377. ;hl = hle / 32
  378.         sla e : adc hl,hl
  379.         sla e : adc hl,hl
  380.         sla e : adc hl,hl
  381.         ld (framelength),hl
  382.         ex de,hl
  383.         ld bc,CC2
  384.         call uintmul16
  385.         xor a
  386.         rl h
  387.         adc a,e
  388.         ld (waitspincount),a
  389.         ret
  390.  
  391. swapinterrupthandler
  392.         di
  393.         ld hl,.store
  394.         ld de,0x38
  395.         ld b,3
  396. .loop   ld a,(de)
  397.         ld c,(hl)
  398.         ld (hl),a
  399.         ld a,c
  400.         ld (de),a
  401.         inc hl
  402.         inc de
  403.         djnz .loop
  404.         ei
  405.         ret
  406. .store  jp lightweightinterrupthandler
  407.  
  408. lightweightinterrupthandler
  409.         push af
  410.         push hl
  411.         ld hl,initwaitspincount.spincount
  412.         inc (hl)
  413.         pop hl
  414.         pop af
  415.         ei
  416.         ret
  417.  
  418. midloadfile
  419. ;hl = input file name
  420. ;out: zf=1 if loaded, zf=0 otherwise
  421.         call midinitport
  422. ;reset the reciever
  423.         ld d,255
  424.         call midsendbyte
  425. ;load and parse the file
  426.         ex de,hl
  427.         call memorystreamloadfile
  428.         ret nz
  429.         ld hl,midplayer
  430.         ld de,midplayer+1
  431.         ld bc,MIDPLAYER-1
  432.         ld (hl),0
  433.         ldir
  434.         call memorystreamstart
  435.         ld b,midheadersigsize
  436.         ld de,midheadersig
  437.         call midchecksignature
  438.         jp nz,memorystreamfree ;sets zf=0
  439.         memory_stream_read_2 c,a
  440.         ld (midplayer.filetype),a
  441.         memory_stream_read_2 c,a
  442.         ld (midplayer.trackcount),a
  443.         add a,-MIDMAXTRACKS-1
  444.         sbc a,a
  445.         ret nz
  446.         memory_stream_read_2 b,c
  447.         ld de,VSYNC_MCS
  448.         call uintmul16
  449.         add hl,hl : rl de
  450.         add hl,hl : rl de
  451.         ld (midplayer.ticksperqnoteXupdatelen+0),hl
  452.         ld (midplayer.ticksperqnoteXupdatelen+2),de
  453.         call midloadtracks
  454.         jp nz,memorystreamfree ;sets zf=0
  455.         call midsetprogressdelta
  456.         ld de,0
  457.         ld hl,14
  458.         call memorystreamseek
  459.         call midloadtracks
  460.         jp nz,memorystreamfree ;sets zf=0
  461.         ld hl,DEFAULT_QNOTE_DURATION_MCS%65536
  462.         ld de,DEFAULT_QNOTE_DURATION_MCS/65536
  463.         call setticksperupdate
  464.         xor a
  465.         ret
  466.  
  467. midchecksignature
  468. ;b = byte count
  469. ;de = signature
  470. ;out: zf=1 if ok, zf=0 otherwise
  471.         ld hl,(memorystreamcurrentaddr)
  472. .loop   memory_stream_read_byte c
  473.         ld a,(de)
  474.         cp c
  475.         ret nz
  476.         inc de
  477.         djnz .loop
  478.         ld (memorystreamcurrentaddr),hl
  479.         ret
  480.  
  481. midloadtracks
  482.         ld ix,midplayer.tracks
  483.         ld iy,(midplayer.trackcount)
  484. .loop   ld b,midtracksigsize
  485.         ld de,midtracksig
  486.         call midchecksignature
  487.         ret nz
  488.         call memorystreamread4
  489.         ld l,b
  490.         ld h,c
  491.         push hl
  492.         ld e,a
  493.         push de
  494.         call memorystreamgetpos
  495.         push de
  496.         push hl
  497.         ld hl,(memorystreamcurrentaddr)
  498.         call midreadvarint
  499.         ld (memorystreamcurrentaddr),hl
  500.        
  501.         ld b,0
  502.     sla de : rl bc
  503.     sla de : rl bc
  504.        
  505.         ld (ix+MIDTRACK.nexteventtick+0),de
  506.         ld (ix+MIDTRACK.nexteventtick+2),c
  507.         call memorystreamgetpos
  508.         ld (ix+MIDTRACK.streamoffset+0),hl
  509.         ld (ix+MIDTRACK.streamoffset+2),de
  510.         pop hl
  511.         pop de
  512.         pop bc
  513.         add hl,bc
  514.         ex de,hl
  515.         pop bc
  516.         adc hl,bc
  517.         ex de,hl
  518.         call memorystreamseek
  519.         ld bc,MIDTRACK
  520.         add ix,bc
  521.         dec iyl
  522.         jp nz,.loop
  523.         ret
  524.  
  525. midmute
  526.         ld e,0xb0
  527.         ld b,MIDCHANNELS
  528. .loop   push bc
  529.         ld d,e
  530.         ld hl,123*256
  531.         call midsend3 ;notes off
  532.         ld d,e
  533.         ld hl,120*256
  534.         call midsend3 ;sounds off
  535.         ld d,e
  536.         ld hl,121*256
  537.         call midsend3 ;controllers off
  538.         pop bc
  539.         inc e
  540.         djnz .loop
  541.         ret
  542.  
  543. midunload
  544.         call midmute
  545.         jp memorystreamfree
  546.  
  547. midplay
  548.         YIELD
  549. ;advance tick counter
  550.         ld hl,(midplayer.tickcounter+0)
  551.         ld de,(midplayer.ticksperupdate+0)
  552.         add hl,de
  553.         ld (midplayer.tickcounter+0),hl
  554.         ex de,hl
  555.         ld hl,(midplayer.tickcounter+2)
  556.         ld bc,(midplayer.ticksperupdate+2)
  557.         adc hl,bc
  558.         ld (midplayer.tickcounter+2),hl
  559.         ex de,hl
  560.         call midgetprogress
  561.         call updateprogress
  562. ;iterate through the tracks
  563.         ld ix,midplayer.tracks
  564.         ld a,(midplayer.trackcount)
  565.         ld b,a
  566.         ld c,0
  567. .trackloop
  568.         bit 7,(ix+MIDTRACK.streamoffset+3)
  569.         jr nz,.skiptrack
  570.         ld c,255
  571.         ld hl,(midplayer.tickcounter+0)
  572.         ld de,(ix+MIDTRACK.nexteventtick+0)
  573.         sub hl,de
  574.         ld hl,(midplayer.tickcounter+2)
  575.         ld de,(ix+MIDTRACK.nexteventtick+2)
  576.         sbc hl,de
  577.         jr c,.skiptrack
  578.         push bc
  579.         call midhandletrackevent
  580.         pop bc
  581.         jr .trackloop
  582. .skiptrack
  583.         ld de,MIDTRACK
  584.         add ix,de
  585.         djnz .trackloop
  586.         ld a,c
  587.         or a
  588.         ret
  589.  
  590. midreadvarint
  591. ;hl = memory stream addr
  592. ;out: cde = number, hl = memory stream addr
  593.         ld de,0
  594.         ld c,0
  595. .loop   memory_stream_read_byte b
  596.         ld a,e
  597.         rrca
  598.         xor b
  599.         and 0x80
  600.         xor b
  601.         rr c
  602.         rr de
  603.         ld c,d
  604.         ld d,e
  605.         ld e,a
  606.         bit 7,b
  607.         jr nz,.loop
  608.         ret
  609.  
  610.         macro process_midi_event call_send_byte,call_send_2,call_send_3
  611. ;ix = track
  612. ;hl = memory stream address
  613.         memory_stream_read_byte b
  614.         bit 7,b
  615.         jr z,.gotdatabyte
  616.         ld (ix+MIDTRACK.lastcommand),b
  617.         memory_stream_read_byte d
  618.         jr .handlecommand
  619. .gotdatabyte
  620.         ld d,b
  621.         ld b,(ix+MIDTRACK.lastcommand)
  622. .handlecommand
  623.         ld a,b
  624.         rrca
  625.         rrca
  626.         rrca
  627.         rrca
  628.         and 7
  629.         ld c,a
  630.         add a,a
  631.         add a,c
  632.         ld (.commandtable),a
  633. .commandtable=$+1
  634.         jr $
  635.         jp .send3 ; 8 Note Off
  636.         jp .send3 ; 9 Note On
  637.         jp .send3 ; A Polyphonic Pressure
  638.         jp .send3 ; B Control Change   
  639.         jp .send2 ; C Program Change
  640.         jp .send2 ; D Channel Pressure
  641.         jp .send3 ; E Pitch Bend
  642. ;;;;;;;;;;;;;;;;;;; F System
  643.         ld a,b
  644.         cp 0xff
  645.         jp z,.handlemeta
  646.         cp 0xf0
  647.         jp nz,.finalize
  648.         call midreadvarint
  649.         ld d,0xf0
  650.         call_send_byte
  651. .sendloop
  652.         memory_stream_read_byte e
  653.         ld d,e
  654.         call_send_byte
  655.         ld a,e
  656.         cp 0xf7
  657.         jr nz,.sendloop
  658.         ld (memorystreamcurrentaddr),hl
  659.         jr .finalize
  660. .handlemeta
  661.         ld a,d
  662.         cp 0x2f
  663.         jr z,.markdone
  664.         cp 0x51
  665.         jr z,.setduration
  666.         call midreadvarint
  667.         ld a,e
  668.         or d
  669.         jr z,.finalize
  670.         ld a,e
  671.         dec de
  672.         inc d
  673.         ld c,d
  674.         ld b,a
  675. .skiploop
  676.         bit 6,h
  677.         call nz,memorystreamnextpage
  678.         inc hl
  679.         djnz .skiploop
  680.         dec c
  681.         jr nz,.skiploop
  682.         ld (memorystreamcurrentaddr),hl
  683.         jr .finalize
  684. .markdone
  685.         set 7,(ix+MIDTRACK.streamoffset+3)
  686.         ret
  687. .setduration
  688.         call midreadvarint
  689.         memory_stream_read_byte a
  690.         memory_stream_read_byte d
  691.         memory_stream_read_byte e
  692.         ld (memorystreamcurrentaddr),hl
  693.         ex de,hl
  694.         ld e,a
  695.         ld d,0
  696.         push ix
  697.         call setticksperupdate
  698.         pop ix
  699.         jr .finalize
  700. .send2  ld (memorystreamcurrentaddr),hl
  701.         ld l,d
  702.         ld h,b
  703.         call_send_2
  704.         jr .finalize
  705. .send3  memory_stream_read_byte e
  706.         ld (memorystreamcurrentaddr),hl
  707.         ex de,hl
  708.         ld d,b
  709.         call_send_3
  710. .finalize
  711.         ld hl,(memorystreamcurrentaddr)
  712.         call midreadvarint
  713.         ld (memorystreamcurrentaddr),hl
  714.         ld b,0
  715.         sla de : rl bc
  716.         sla de : rl bc
  717.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  718.         add hl,de
  719.         ld (ix+MIDTRACK.nexteventtick+0),hl
  720.         ld hl,(ix+MIDTRACK.nexteventtick+2)
  721.         adc hl,bc
  722.         ld (ix+MIDTRACK.nexteventtick+2),hl
  723.         endm
  724.  
  725. midhandletrackevent
  726. ;ix = track
  727.         ld hl,(ix+MIDTRACK.streamoffset+0)
  728.         ld de,(ix+MIDTRACK.streamoffset+2)
  729.         call memorystreamseek
  730.         process_midi_event <call midsendbyte>,<call midsend2>,<call midsend3>
  731.         call memorystreamgetpos
  732.         ld (ix+MIDTRACK.streamoffset+0),hl
  733.         ld (ix+MIDTRACK.streamoffset+2),de
  734.         ret
  735.  
  736. midgetprogress
  737. ;dehl = ticks
  738. ;out: a = progress
  739.         ld a,e
  740.         add hl,hl : rla
  741.         add hl,hl : rla
  742.         add hl,hl : rla
  743.         add hl,hl : rla
  744.         ret
  745.  
  746. midsetprogressdelta
  747.         ld ix,midplayer.tracks
  748.         ld a,(midplayer.trackcount)
  749.         ld b,a
  750.         ld c,0
  751. .trackloop
  752.         push bc
  753.         ld hl,(ix+MIDTRACK.streamoffset+0)
  754.         ld de,(ix+MIDTRACK.streamoffset+2)
  755.         call memorystreamseek
  756. .eventloop
  757.         call midadvancetrack
  758.         bit 7,(ix+MIDTRACK.streamoffset+3)
  759.         jr z,.eventloop
  760.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  761.         ld de,(ix+MIDTRACK.nexteventtick+2)
  762.         call midgetprogress
  763.         pop bc
  764.         cp c
  765.         jr c,$+3
  766.         ld c,a
  767.         ld de,MIDTRACK
  768.         add ix,de
  769.         djnz .trackloop
  770.         ld a,c
  771.         jp setprogressdelta
  772.  
  773. midadvancetrack
  774. ;ix = track
  775.         ld hl,(memorystreamcurrentaddr)
  776.         process_midi_event < >,< >,< >
  777.         ret
  778.  
  779. setticksperupdate
  780. ;dehl = qnote duration in mcs
  781.         exx
  782.         ld hl,(midplayer.ticksperqnoteXupdatelen+0)
  783.         ld de,(midplayer.ticksperqnoteXupdatelen+2)
  784.         call uintdiv32
  785.         ld (midplayer.ticksperupdate+0),hl
  786.         ld (midplayer.ticksperupdate+2),de
  787.         ret
  788.  
  789. midheadersig
  790.         db "MThd",0,0,0,6
  791. midheadersigsize = $-midheadersig
  792. midtracksig
  793.         db "MTrk"
  794. midtracksigsize = $-midtracksig
  795. initokstr
  796.         db "OK\r\n",0
  797. playernamestr
  798.         db "ProTracker/MIDI UART",0
  799. end
  800.  
  801. titlestr ds TITLELENGTH+1
  802. isplayingmidfile ds 1
  803. framelength ds 2
  804. waitspincount ds 1
  805. midplayer MIDPLAYER
  806.  
  807.         assert $ <= PLAYEREND ;ensure everything is within the player page
  808.  
  809.         savebin "pt3.bin",begin,end-begin
  810.