; ._. .__. .____. .________. ._. ; _! !_! !__! !____! !________! !_ ; ; 8 channel cheapo midi -> cv converter ; ._. .__. .____. .________. ; ________! !_! !__! !____! !_____ processor 16F84 include __config _WDT_OFF random EQU 0x10 ; random state plen EQU 0x11 ; cycle len voicestate EQU 0x12 ; on/off midibyte EQU 0x13 midifirst EQU 0x14 midinote EQU 0x15 midivelocity EQU 0x16 temp EQU 0x17 bitcount EQU 0x18 vnote EQU 0x19 ; array for note nums [+8] vtime EQU 0x21 ; array for times [+8] voiceid EQU 0x29 midipin EQU 00 ; input pin mididebugpin EQU 01 ;--------------------------------- ; .-----------. .---- ; ! ! ! ; !<- vtime ->! ! ; ----' '---' ; TMR0 = 0...............0.... ; <--- plen ----> ;--------------------------------- main: movlw B'00000001' tris PORTA ; port a is mostly output movlw B'00000000' tris PORTB ; port b is output movlw B'10000011' ; timer prescale /16 option ; init stuff movlw D'4' movwf random movlw D'127' movwf plen clrf TMR0 clrf voicestate ; test... ; movlw 0x90 ; movwf midifirst ; movlw 0x40 ; movwf midinote ; movlw 0x01 ; movwf midivelocity ; ; call noteon ; ; movlw 0x92 ; movwf midifirst ; movlw 0x01 ; movwf midinote ; ; call noteon ;--------------------------------- begin: call checkmidi movf TMR0,w ; if timer has wrapped btfss STATUS,Z goto carryon movlw 0xff movwf PORTB ; clear all out pins ;incf plen,f carryon: ; update all outputs ; 1 clrf voiceid call updatepw ; 2 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 3 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 4 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 5 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 6 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 7 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw ; 8 call checkmidi ; need to do this sporadically incf voiceid,f call updatepw goto begin updatepw: ; get the midi note movlw vnote ; copy vnotes base address movwf FSR ; to the fsr movf bitcount,w ; load the voice num addwf FSR,f ; add it to the fsr movf 0,w ; load the midi note movwf plen ; indirect address ; try using the midi note as the pulsewidth ; can correct and tune in circuitry, but will ; this need to be logarithmic? rlf plen,f rlf plen,f rlf plen,f ; is it time to flip? movf TMR0,w subwf plen,w btfsc STATUS,C return ; set the right output bit movf PORTB,w call clrbit movwf PORTB return ;--------------------------------------------------------- ; dodgy 8 bit random rand: movlw 0x1d addwf random,f clrc rlf random,f skpnc xorwf random,f retlw 0 ;------------------------------------------------------ ; check for, read and interpret a midi message checkmidi: btfsc PORTA,midipin ; is start bit low? retlw 0 call readmidibyte ; read first byte movf midibyte,w movwf midifirst call readmidibyte ; read note num movf midibyte,w movwf midinote call readmidibyte ; read velocity movf midibyte,w movwf midivelocity ; check if it's a note on movlw 0xf0 ; mask lower nibble out andwf midifirst,w sublw 0x90 ; compare with note on btfsc STATUS,Z goto noteon ; check if it's a note off movlw 0xf0 ; mask lower nibble out andwf midifirst,w sublw 0x80 ; compare with note off btfsc STATUS,Z goto noteoff ; i don't understand this message retlw 0 noteon: bsf PORTA,mididebugpin movf midivelocity,w ; check for velocity=0 btfsc STATUS,Z ; style note off (roland) goto noteoff movf midifirst,w ; get the channel num andlw 0x0f ; mask off the top nibble movwf bitcount ; setup bitcount movf voicestate,w ; set the state call setbit ; set the bit movwf voicestate movlw vnote ; copy vnotes base address movwf FSR ; to the fsr movf bitcount,w ; load the voice num addwf FSR,f ; add it to the fsr movf midinote,w ; load the current midi note movwf 0 ; indirect address movlw vtime ; get vtime base address movwf FSR ; copy it to the fsr movf bitcount,w ; load the voice num addwf FSR,f ; add it to the fsr movlw 0x00 ; load 0 movwf 0 ; set it to be the time retlw 1 noteoff: bcf PORTA,mididebugpin movf midifirst,w ; get the channel num andlw 0x0f ; mask off the top nibble movwf bitcount ; setup bitcount movf voicestate,w call clrbit ; clear the bit movwf voicestate movlw vnote ; copy vnotes base address movwf FSR ; to the fsr movf bitcount,w ; load the voice num addwf FSR,f ; add it to the fsr clrf 0 ; clear the midi note retlw 1 ;------------------------------------------- ; low level byte reading for midi messages readmidibyte: movlw D'24' ; delay 73 clocks movwf temp ; | decfsz temp,f ; | goto $-1 ; end delay movlw D'8' ; read 8 bits movwf bitcount readbits: rrf midibyte,f ; set up for the next bit btfss PORTA,midipin goto pad ; pad keeps proc time same bsf midibyte,7 nop goto endread pad: bcf midibyte,7 nop nop endread: movlw D'23' ; delay movwf temp ; before reading decfsz temp,f ; next bit goto $-1 decfsz bitcount,f ; have we finished? goto readbits nop ; delay to ignore stop bit nop nop nop nop nop movlw D'26' ; delay 79 clocks: 2 + (25 * 3 + 1 * 2) movwf temp ; | decfsz temp,f ; | goto $-1 ; end delay return ;------------------------------------------ ; sets a bit of w given by bitselect ; is there a much simpler way with bsf i'm missing? ; could use rlf, but this is less instructions setbit: movwf temp movf bitcount,w call settable iorwf temp,f movf temp,w return settable: addwf 2,f retlw B'00000001' retlw B'00000010' retlw B'00000100' retlw B'00001000' retlw B'00010000' retlw B'00100000' retlw B'01000000' retlw B'10000000' ;------------------------------------------ ; clear a bit of w given by bitselect clrbit: movwf temp movf bitcount,w call clrtable andwf temp,f movf temp,w return clrtable: addwf 2,f retlw B'11111110' retlw B'11111101' retlw B'11111011' retlw B'11110111' retlw B'11101111' retlw B'11011111' retlw B'10111111' retlw B'01111111' end