?login_element?

Subversion Repositories NedoOS

Rev

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