6502 MIDI file player
I’ve build a MIDI interface adapter for my single board computer. The output TXDB of my SC26C92 DUART is connected to the TTL MIDI input of a General Midi wavetable synthesizer (Roland SCB-55). I have 500kHz connected to IP5 which generates the needed 31,250 baud. Helpful schematics can be found at:
http://serdaco.com/files/BUILDINSTRUCTI ... LBOARD.pdfAttachment:
midi board.jpg [ 1.63 MiB | Viewed 7951 times ]
The hardware and software is working, and I can send midi messages to serial port B.
Ultimately I’d like to program an interrupt based MIDI file player which plays standard GM (General MIDI) files.
I researched how MIDI file format 0 works and want to tackle the task in separate steps.
• A conversion program from VLQ (variable length quantity) to 32-bit value.
• A zero page pointer to grab MIDI data.
• A way to separate delta times and MIDI events.
• A lookup table to determine how many bytes each MIDI event takes.
• A counter to compare with delta times and execute MIDI events.
• Send the appropriate bytes to UART B at the right times.
• Make the whole process interrupt driven.
The first step: decoding variable length quantity (for MIDI) in W65C02S assembly.
Pseudo code:
1. Initialize the variable which will hold the value. Set it to 0. We'll call this variable 'result'.
2. Read the next byte of the Variable Length quantity from the MIDI file.
3. Shift all of the bits in 'result' 7 places to the left. (ie, Multiply 'result' by 128).
4. Logically OR 'result' with the byte that was read in, but first mask off bit #7 of the byte. (ie, AND the byte with hexadecimal 7F before you OR with 'result'. But make sure you save the original value of the byte for the test in the next step).
5. Test if bit #7 of the byte is set. (ie, Is the byte AND hexadecimal 80 equal to hexadecimal 80)? If so, loop back to step #2. Otherwise, you're done, and 'result' now has the appropriate value.
Code:
!cpu 65C02
* = $0400
;variables
result
!byte $00,$00,$00,$00 ;4 byte value
start
stz result ;initialize result
stz result+1
stz result+2
stz result+3
ldx #0 ;initialize byte counter
lb1 ldy #7
lb2 asl result ;shift all bits to the left
rol result+1 ;preserving the carry
rol result+2
rol result+3
dey ;7 times?
bne lb2 ;no? repeat
lda midi,x ;load byte from midi data (msb first)
bpl lb3 ;finish reading bytes if bit 7=0
and #$7f ;mask 7th bit
ora result ;add the value to the 7 freed bits of result
sta result
inx
bra lb1 ;get the next byte from midi
lb3 ora result ;add the value to the 7 freed bits of result
sta result
brk
* = $0500
midi
!byte $81,$80,$00,$00 ;81,80,00 vlq equal to $4000
I’ve tested with various values, and it seems to work as it should. But perhaps the routine can be optimized? I want to save zero page space, so I didn’t put the variables there.
Sources:
https://web.archive.org/web/20051129113 ... e/vari.htmhttp://www.music.mcgill.ca/~ich/classes ... ormat.html