?login_element?

Subversion Repositories NedoOS

Rev

Rev 2033 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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.         ld (ix+MIDTRACK.nexteventtick+0),de
  501.         ld (ix+MIDTRACK.nexteventtick+2),c
  502.         call memorystreamgetpos
  503.         ld (ix+MIDTRACK.streamoffset+0),hl
  504.         ld (ix+MIDTRACK.streamoffset+2),de
  505.         pop hl
  506.         pop de
  507.         pop bc
  508.         add hl,bc
  509.         ex de,hl
  510.         pop bc
  511.         adc hl,bc
  512.         ex de,hl
  513.         call memorystreamseek
  514.         ld bc,MIDTRACK
  515.         add ix,bc
  516.         dec iyl
  517.         jp nz,.loop
  518.         ret
  519.  
  520. midmute
  521.         ld e,0xb0
  522.         ld b,MIDCHANNELS
  523. .loop   push bc
  524.         ld d,e
  525.         ld hl,123*256
  526.         call midsend3 ;notes off
  527.         ld d,e
  528.         ld hl,120*256
  529.         call midsend3 ;sounds off
  530.         ld d,e
  531.         ld hl,121*256
  532.         call midsend3 ;controllers off
  533.         pop bc
  534.         inc e
  535.         djnz .loop
  536.         ret
  537.  
  538. midunload
  539.         call midmute
  540.         jp memorystreamfree
  541.  
  542. midplay
  543.         YIELD
  544. ;advance tick counter
  545.         ld hl,(midplayer.tickcounter+0)
  546.         ld de,(midplayer.ticksperupdate+0)
  547.         add hl,de
  548.         ld (midplayer.tickcounter+0),hl
  549.         ex de,hl
  550.         ld hl,(midplayer.tickcounter+2)
  551.         ld bc,(midplayer.ticksperupdate+2)
  552.         adc hl,bc
  553.         ld (midplayer.tickcounter+2),hl
  554.         ex de,hl
  555.         call midgetprogress
  556.         call updateprogress
  557. ;iterate through the tracks
  558.         ld ix,midplayer.tracks
  559.         ld a,(midplayer.trackcount)
  560.         ld b,a
  561.         ld c,0
  562. .trackloop
  563.         bit 7,(ix+MIDTRACK.streamoffset+3)
  564.         jr nz,.skiptrack
  565.         ld c,255
  566.         ld hl,(midplayer.tickcounter+0)
  567.         ld de,(ix+MIDTRACK.nexteventtick+0)
  568.         sub hl,de
  569.         ld hl,(midplayer.tickcounter+2)
  570.         ld de,(ix+MIDTRACK.nexteventtick+2)
  571.         sbc hl,de
  572.         jr c,.skiptrack
  573.         push bc
  574.         call midhandletrackevent
  575.         pop bc
  576.         jr .trackloop
  577. .skiptrack
  578.         ld de,MIDTRACK
  579.         add ix,de
  580.         djnz .trackloop
  581.         ld a,c
  582.         or a
  583.         ret
  584.  
  585. midreadvarint
  586. ;hl = memory stream addr
  587. ;out: cde = number, hl = memory stream addr
  588.         ld de,0
  589.         ld c,0
  590. .loop   memory_stream_read_byte b
  591.         ld a,e
  592.         rrca
  593.         xor b
  594.         and 0x80
  595.         xor b
  596.         rr c
  597.         rr de
  598.         ld c,d
  599.         ld d,e
  600.         ld e,a
  601.         bit 7,b
  602.         jr nz,.loop
  603.         ret
  604.  
  605.         macro process_midi_event call_send_byte,call_send_2,call_send_3
  606. ;ix = track
  607. ;hl = memory stream address
  608.         memory_stream_read_byte b
  609.         bit 7,b
  610.         jr z,.gotdatabyte
  611.         ld (ix+MIDTRACK.lastcommand),b
  612.         memory_stream_read_byte d
  613.         jr .handlecommand
  614. .gotdatabyte
  615.         ld d,b
  616.         ld b,(ix+MIDTRACK.lastcommand)
  617. .handlecommand
  618.         ld a,b
  619.         rrca
  620.         rrca
  621.         rrca
  622.         rrca
  623.         and 7
  624.         ld c,a
  625.         add a,a
  626.         add a,c
  627.         ld (.commandtable),a
  628. .commandtable=$+1
  629.         jr $
  630.         jp .send3 ; 8 Note Off
  631.         jp .send3 ; 9 Note On
  632.         jp .send3 ; A Polyphonic Pressure
  633.         jp .send3 ; B Control Change   
  634.         jp .send2 ; C Program Change
  635.         jp .send2 ; D Channel Pressure
  636.         jp .send3 ; E Pitch Bend
  637. ;;;;;;;;;;;;;;;;;;; F System
  638.         ld a,b
  639.         cp 0xff
  640.         jp z,.handlemeta
  641.         cp 0xf0
  642.         jp nz,.finalize
  643.         call midreadvarint
  644.         ld d,0xf0
  645.         call_send_byte
  646. .sendloop
  647.         memory_stream_read_byte e
  648.         ld d,e
  649.         call_send_byte
  650.         ld a,e
  651.         cp 0xf7
  652.         jr nz,.sendloop
  653.         ld (memorystreamcurrentaddr),hl
  654.         jr .finalize
  655. .handlemeta
  656.         ld a,d
  657.         cp 0x2f
  658.         jr z,.markdone
  659.         cp 0x51
  660.         jr z,.setduration
  661.         call midreadvarint
  662.         ld a,e
  663.         or d
  664.         jr z,.finalize
  665.         ld a,e
  666.         dec de
  667.         inc d
  668.         ld c,d
  669.         ld b,a
  670. .skiploop
  671.         bit 6,h
  672.         call nz,memorystreamnextpage
  673.         inc hl
  674.         djnz .skiploop
  675.         dec c
  676.         jr nz,.skiploop
  677.         ld (memorystreamcurrentaddr),hl
  678.         jr .finalize
  679. .markdone
  680.         set 7,(ix+MIDTRACK.streamoffset+3)
  681.         ret
  682. .setduration
  683.         call midreadvarint
  684.         memory_stream_read_byte a
  685.         memory_stream_read_byte d
  686.         memory_stream_read_byte e
  687.         ld (memorystreamcurrentaddr),hl
  688.         ex de,hl
  689.         ld e,a
  690.         ld d,0
  691.         push ix
  692.         call setticksperupdate
  693.         pop ix
  694.         jr .finalize
  695. .send2  ld (memorystreamcurrentaddr),hl
  696.         ld l,d
  697.         ld h,b
  698.         call_send_2
  699.         jr .finalize
  700. .send3  memory_stream_read_byte e
  701.         ld (memorystreamcurrentaddr),hl
  702.         ex de,hl
  703.         ld d,b
  704.         call_send_3
  705. .finalize
  706.         ld hl,(memorystreamcurrentaddr)
  707.         call midreadvarint
  708.         ld (memorystreamcurrentaddr),hl
  709.         ld b,0
  710.         sla de : rl bc
  711.         sla de : rl bc
  712.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  713.         add hl,de
  714.         ld (ix+MIDTRACK.nexteventtick+0),hl
  715.         ld hl,(ix+MIDTRACK.nexteventtick+2)
  716.         adc hl,bc
  717.         ld (ix+MIDTRACK.nexteventtick+2),hl
  718.         endm
  719.  
  720. midhandletrackevent
  721. ;ix = track
  722.         ld hl,(ix+MIDTRACK.streamoffset+0)
  723.         ld de,(ix+MIDTRACK.streamoffset+2)
  724.         call memorystreamseek
  725.         process_midi_event <call midsendbyte>,<call midsend2>,<call midsend3>
  726.         call memorystreamgetpos
  727.         ld (ix+MIDTRACK.streamoffset+0),hl
  728.         ld (ix+MIDTRACK.streamoffset+2),de
  729.         ret
  730.  
  731. midgetprogress
  732. ;dehl = ticks
  733. ;out: a = progress
  734.         ld a,e
  735.         add hl,hl : rla
  736.         add hl,hl : rla
  737.         add hl,hl : rla
  738.         add hl,hl : rla
  739.         ret
  740.  
  741. midsetprogressdelta
  742.         ld ix,midplayer.tracks
  743.         ld a,(midplayer.trackcount)
  744.         ld b,a
  745.         ld c,0
  746. .trackloop
  747.         push bc
  748.         ld hl,(ix+MIDTRACK.streamoffset+0)
  749.         ld de,(ix+MIDTRACK.streamoffset+2)
  750.         call memorystreamseek
  751. .eventloop
  752.         call midadvancetrack
  753.         bit 7,(ix+MIDTRACK.streamoffset+3)
  754.         jr z,.eventloop
  755.         ld hl,(ix+MIDTRACK.nexteventtick+0)
  756.         ld de,(ix+MIDTRACK.nexteventtick+2)
  757.         call midgetprogress
  758.         pop bc
  759.         cp c
  760.         jr c,$+3
  761.         ld c,a
  762.         ld de,MIDTRACK
  763.         add ix,de
  764.         djnz .trackloop
  765.         ld a,c
  766.         jp setprogressdelta
  767.  
  768. midadvancetrack
  769. ;ix = track
  770.         ld hl,(memorystreamcurrentaddr)
  771.         process_midi_event < >,< >,< >
  772.         ret
  773.  
  774. setticksperupdate
  775. ;dehl = qnote duration in mcs
  776.         exx
  777.         ld hl,(midplayer.ticksperqnoteXupdatelen+0)
  778.         ld de,(midplayer.ticksperqnoteXupdatelen+2)
  779.         call uintdiv32
  780.         ld (midplayer.ticksperupdate+0),hl
  781.         ld (midplayer.ticksperupdate+2),de
  782.         ret
  783.  
  784. midheadersig
  785.         db "MThd",0,0,0,6
  786. midheadersigsize = $-midheadersig
  787. midtracksig
  788.         db "MTrk"
  789. midtracksigsize = $-midtracksig
  790. initokstr
  791.         db "OK\r\n",0
  792. playernamestr
  793.         db "ProTracker/MIDI UART",0
  794. end
  795.  
  796. titlestr ds TITLELENGTH+1
  797. isplayingmidfile ds 1
  798. framelength ds 2
  799. waitspincount ds 1
  800. midplayer MIDPLAYER
  801.  
  802.         assert $ <= PLAYEREND ;ensure everything is within the player page
  803.  
  804.         savebin "pt3.bin",begin,end-begin
  805.