; OUT / PIN / PORT MAPPING: ;--------------------- ; out0 = pin4 = RA2 ; out1 = pin3 = RA1 ; out2 = pin2 = RA0 ; out3 = pin5 = RA3 ; out4 = pin6 = RA4 ; out5 = pin7 = RA5 ; out6 = pin28 = RB7 ; out7 = pin27 = RB6 ; out8 = pin26 = RB5 ; out9 = pin25 = RB4 ; out10 = pin24 = RB3 ; out11 = pin23 = RB2 ; out12 = pin22 = RB1 ; out13 = pin21 = RB0 ; out14 = pin16 = RC5 ; out15 = pin15 = RC4 ; LED = pin11 = RC0 ; Changed: ; * baudrate -> 31250 (MIDI) ; * serial receive -> lo priority interrupt ; 1. interrupt priority register ; 2. move ISR check ; 3. check ISR return ; * TIMER3 config ; * TIMER3 preliminary ISR ; * TIMER3 test ; - no visible jitter on scope ; - 35 us pule when setting timer to 0xFE00 ; - no conflict with serial stuff ; * removed CR stuff from serial handling ; * Clock mode -> HS (PLL OFF) ; We're at 20MHz.... ; * check serial loopback -> OK (clock was wrong) ; * check where receive is stuck -> we get frame errors (lata:3) ; * wrong baudrate? ; * more midi cables... ; * timer3 enabled ; * MIDI FSM pseudocode ; * MIDI FSM ; * functional testing MIDI FSM ; * BUG: note on w/ velocity=0 -> noteoff ; * remove LATA heartbeats ; * hum, mixed noteOn en noteOff midi codes ; * memory map reorganisation (merge with timer project) ; * check memory paging & memory declaration in asm -> looks ok ; * RA4 is TRIStAting? (no prob anymore) ; * backport stuff from timer setup... ; * reorganize output latches -> OutsA, OutsB ; * back to simulation: ; timer ISR -> poll loop ; midiIn -> MidiTestByte ; * lookup midiNote -> timer# ; * queue stuff in timer queue ; * back to reality ; * LATC: for heartbeats DBG! ; * LATA:0 is ALIVE AND KICKING (C#) ; * 4 CHANNELs KICKING ; * but some things seem to be swapped when reading PM tables ; solution: DB instead of DATA directive for single-byte data ; * another slight speedup ; * velocity 7F does not work! (but cmd is queue'd) ; -> back to simulation ... (now with conditional assembler) ; the fix: post increment wraps... ; * move heartbeat to free LATC pins ; * noteOn velo=0 -> noteOff FIXED ; * 8 Channels -> LATA:6 and LATA:7 -> LATC:0 and LATC:1 ; * another speedup (engaging timer3 LSB) ; * dropped note-offs found ; -> separate shadow registers for separate interrupt priorities ; -> not a problem in software, but a mechanical one :/ ; * map noteOffs nicely ; * rename project -> midima ; DOING: ; * buy 10MHz XTal and CLK -> 40MHz (HSPLL mode) ; * init, error LED ; * pin map ; * 3 byte output masks..., needs some major changes ; * cmd queue 3bytes/entry, move velocity lookup outside timer ISR ; TODO: ; error light ; adjust midi channel ; remove note-off stuff ; * speedup timer3 ; * MIDI FSM init !! (?) ; * timer ISR overdrive test ; ; (ç)20030906 j#|@ ; Specs: ; ; - midi input (31250 bps) ; - 8 pulse outputs, pulse duration controlled by note-on velocity ; - 16 bit pulse counter resolution, full velocity->duration lookup table ; per output ; - pulse counter clock: 25us ; - 100% deterministic pulse duration ; - 8 hold outputs (note-on -> note off) ; - single chip, 28 pin DIL, highly integrated solution ;============================================================================= ; Filename: p18_tiri.asm ;============================================================================= ; Author: Johannes Taelman ; Company: Johannes Taelman ; Revision: 1.00 ; Date: September 6, 2003 ; Assembled using MPASMWIN V3.20 ;============================================================================= ; Include Files: p18f252.inc V1.3 ;============================================================================= ; Uses ; PIC18XXX USART example code for with transmit and receive interrupts. ; Received data is put into a buffer, called RxBuffer. ; Receive uses low priority interrupts, ; Timer usees high priority. ; ;============================================================================= ;---------------------------------------------------------------------------- ;This code executes when a reset occurs. ORG 0x0000 ;place code at reset vector ResetVector: bra Main ;go to beginning of program ;---------------------------------------------------------------------------- ;This code executes when a high priority interrupt occurs. ORG 0x0008 HighInt: bra HighIntCode ;go to high priority interrupt routine ;---------------------------------------------------------------------------- ;This code executes when a low priority interrupt occurs. ORG 0x0018 LowInt: movff STATUS,STATUS_TEMP ;save STATUS register movff WREG,WREG_TEMP ;save working register movff BSR,BSR_TEMP ;save BSR register movff FSR0H,FSR0H_TEMP ;save FSR0H register movff FSR0L,FSR0L_TEMP ;save FSR0L register ;test other interrupt flags here btfss PIR1,RCIF ;test for RCIF receive interrupt flag bra LowInt1 ;if RCIF is not set, done with test btfsc PIE1,RCIE ;else test if Rx interrupt enabled bra GetData ;if so, go get received data LowInt1: btfss PIR1,TXIF ;test for TXIF transmit interrupt flag bra LowInt2 ;if TXIF is not set, done with test btfsc PIE1,TXIE ;else test if Tx interrupt is enabled bra PutData ;if so, go transmit data ;can do special error handling here - an unexpected interrupt occurred LowInt2: reset ;error if no valid interrupt so reset ;------------------------------------ ;Read data from the transmit buffer and put into transmit register. PutData: btfss Flags,TxBufEmpty ;check if transmit buffer is empty bra PutDat1 ;if not then go transmit bcf PIE1,TXIE ;else disable Tx interrupt bra EndLowInt PutDat1: rcall GetTxBuffer ;get data from transmit buffer movwf TXREG ;and transmit ;------------------------------------ ;End of low priority interrupt routine restores context. EndLowInt: movff FSR0L_TEMP,FSR0L ;restore FSR0L register movff FSR0H_TEMP,FSR0H ;restore FSR0H register movff BSR_TEMP,BSR ;restore BSR register movff WREG_TEMP,WREG ;restore working register movff STATUS_TEMP,STATUS ;restore STATUS register retfie ;---------------------------------------------------------------------------- ;High priority interrupt routine. HighIntCode: movff FSR0H,FSR0H_SHADOW ;save FSR0H register movff FSR0L,FSR0L_SHADOW ;save FSR0L register ;test other interrupt flags here btfss PIR2,TMR3IF ;test for Timer3 receive interrupt flag bra HighInt1 ;if RCIF is not set, done with test btfsc PIE2,TMR3IE ;else test if Timer3 interrupt enabled bra Timer3ISR ;if so, go get received data ;can do special error handling here - an unexpected interrupt occurred HighInt1: reset ;error if no valid interrupt so reset ;------------------------------------ ;Get received data and write into receive buffer. GetData: ; btg LATA,1; ; toggle LATA:1 = HEARTBEAT btfsc RCSTA,OERR ;if overrun error bra ErrOERR ;then go handle error btfsc RCSTA,FERR ;if framing error bra ErrFERR ;then go handle error btfsc Flags,RxBufFull ;if buffer full bra ErrRxOver ;then go handle error ; btg LATA,1; ; toggle LATA:1 = HEARTBEAT movf RCREG,W ;get received data ; xorlw 0x0d ;compare with ; btfsc STATUS,Z ;check if the same ; bsf Flags,ReceivedCR ;indicate character received ; xorlw 0x0d ;change back to valid data rcall PutRxBuffer ;and put in buffer bra EndLowInt ;error because OERR overrun error bit is set ;can do special error handling here - this code simply clears and continues ErrOERR: bcf RCSTA,CREN ;reset the receiver logic bsf RCSTA,CREN ;enable reception again bsf OutsC,0; error LED movlw 0xFA; ; MIDI RT Start rcall PutTxBuffer ;put data in transmit buffer bra EndLowInt ;error because FERR framing error bit is set ;can do special error handling here - this code simply clears and continues ErrFERR: movf RCREG,W ;discard received data that has error bcf RCSTA,CREN ;reset the receiver logic bsf RCSTA,CREN ;enable reception again bsf OutsC,0; error LED movlw 0xFB; ; MIDI RT Continue rcall PutTxBuffer ;put data in transmit buffer bra EndLowInt ;error because receive buffer is full ;can do special error handling here - this code checks and discards the data ErrRxOver: movf RCREG,W ;discard received data ; xorlw 0x0d ;but compare with ; btfsc STATUS,Z ;check if the same ; bsf Flags,ReceivedCR ;indicate character received bsf OutsC,0; error LED movlw 0xFB; ; MIDI RT Start rcall PutTxBuffer ;put data in transmit buffer bra EndLowInt ;------------------------------------ ;End of high priority interrupt routine restores context. EndHighInt: movff FSR0L_SHADOW,FSR0L ;restore FSR0L register movff FSR0H_SHADOW,FSR0H ;restore FSR0H register retfie FAST ;return and restore context ;---------------------------------------------------------------------------- ;Main routine checks for for reception of a and then calls a routine to ; move the data to transmit back. Main: clrf LATA ; All outputs low clrf LATB ; All outputs low clrf LATC ; All outputs low clrf TRISA ; Config PORTA as all outputs clrf TRISB ; Config PORTB as all outputs movlw 0xC0 ; movwf TRISC ; Config PORTC all outputs + tristate for serial movlw 0x07 ; init adc movwf ADCON1 ; init adc clrf ADCON0 ; poweroff adc clrf TRISA ; Config PORTA again as all outputs clrf OutsA; clrf OutsB; clrf OutsC; bsf LATC,0 ; Light error LED on powerup ; Wait a second with ALoop and BLoop setf TmpM BLoop1: movlw 0xff; ALoop1: decfsz WREG; goto ALoop1 decfsz TmpM; goto BLoop1 bcf LATC,0 ; Been patient enough, dim error LED and start movlw CmdBuff0Timer; movwf CmdBuff0RdIndx; movwf CmdBuff0WrIndx; lfsr FSR0 ,0000h lfsr FSR1 ,0000h rcall SetupSerial ;set up serial port and buffers rcall SetupTimer3 MainLoop: ;;; rcall CopyRxToTx ;if so then move received data ;do other stuff here ; btg LATC,3; ; toggle LATC:3 = IDLE HEARTBEAT ; btg LATA,0; ; toggle LATA:0 = IDLE HEARTBEAT #ifdef simulate nop; your chance to schedule midi cmd movff MidiTestByte,WREG; rcall MidiInParser; rcall Timer3ISR #endif call PollMidiIn; bra MainLoop ;go wait for more data ;---------------------------------------------------------------------------- ;Set up serial port and buffers. SetupSerial: movlw 0xc0 ;set tris bits for Tx and RX iorwf TRISC,F movlw SPBRG_VAL ;set baud rate movwf SPBRG movlw 0x24 ;enable transmission and high baud rate movwf TXSTA movlw 0x90 ;enable serial port and reception movwf RCSTA clrf Flags ;clear all flags rcall InitTxBuffer ;initialize transmit buffer rcall InitRxBuffer ;initialize receive buffer movlw 0x30 ;enable Tx and Rx interrupts movwf PIE1 movlw 0x00 ;set Rx low and Tx low priority movwf IPR1 bsf RCON,IPEN ;enable interrupt priorities movlw 0xc0 ;enable global high and low ints movwf INTCON return ;---------------------------------------------------------------------------- ;Circular buffer routines. ;---------------------------------------------------------------------------- ;Initialize transmit buffer. InitTxBuffer: movlw HIGH TxBuffer ;take high address of transmit buffer movwf TxStartPtrH ;and place in transmit start pointer movwf TxEndPtrH ;and place in transmit end pointer movlw LOW TxBuffer ;take low address of transmit buffer movwf TxStartPtrL ;and place in transmit start pointer movwf TxEndPtrL ;and place in transmit end pointer bcf Flags,TxBufFull ;indicate Tx buffer is not full bsf Flags,TxBufEmpty ;indicate Tx buffer is empty return ;---------------------------------------------- ;Initialize receive buffer. InitRxBuffer: movlw HIGH RxBuffer ;take high address of receive buffer movwf RxStartPtrH ;and place in receive start pointer movwf RxEndPtrH ;and place in receive end pointer movlw LOW RxBuffer ;take low address of receive buffer movwf RxStartPtrL ;and place in receive start pointer movwf RxEndPtrL ;and place in receive end pointer bcf Flags,RxBufFull ;indicate Rx buffer is not full bsf Flags,RxBufEmpty ;indicate Rx buffer is empty return ;---------------------------------------------------------------------------- ;Add a byte (in WREG) to the end of the transmit buffer. PutTxBuffer: bcf PIE1,TXIE ;disable Tx interrupt to avoid conflict btfsc Flags,TxBufFull ;check if transmit buffer full bra ErrTxBufFull ;go handle error if full movff TxEndPtrH,FSR0H ;put EndPointer into FSR0 movff TxEndPtrL,FSR0L ;put EndPointer into FSR0 movwf POSTINC0 ;copy data to buffer ;test if buffer pointer needs to wrap around to beginning of buffer memory movlw HIGH (TxBuffer+TX_BUF_LEN) ;get last address of buffer cpfseq FSR0H ;and compare with end pointer bra PutTxBuf1 ;skip low bytes if high bytes not equal movlw LOW (TxBuffer+TX_BUF_LEN) ;get last address of buffer cpfseq FSR0L ;and compare with end pointer bra PutTxBuf1 ;go save new pointer if at end lfsr 0,TxBuffer ;point to beginning of buffer if at end PutTxBuf1: movff FSR0H,TxEndPtrH ;save new EndPointer high byte movff FSR0L,TxEndPtrL ;save new EndPointer low byte ;test if buffer is full movf TxStartPtrH,W ;get start pointer cpfseq TxEndPtrH ;and compare with end pointer bra PutTxBuf2 ;skip low bytes if high bytes not equal movf TxStartPtrL,W ;get start pointer cpfseq TxEndPtrL ;and compare with end pointer bra PutTxBuf2 bsf Flags,TxBufFull ;if same then indicate buffer full PutTxBuf2: bcf Flags,TxBufEmpty ;Tx buffer cannot be empty bsf PIE1,TXIE ;enable transmit interrupt return ;error because attempting to store new data and the buffer is full ;can do special error handling here - this code simply ignores the byte ErrTxBufFull: bsf PIE1,TXIE ;enable transmit interrupt return ;no save of data because buffer is full ;---------------------------------------------- ;Add a byte (in WREG) to the end of the receive buffer. PutRxBuffer: btfsc Flags,RxBufFull ;check if receive buffer full bra ErrRxBufFull ;go handle error if full movff RxEndPtrH,FSR0H ;put EndPointer into FSR0 movff RxEndPtrL,FSR0L ;put EndPointer into FSR0 movwf POSTINC0 ;copy data to buffer ;test if buffer pointer needs to wrap around to beginning of buffer memory movlw HIGH (RxBuffer+RX_BUF_LEN) ;get last address of buffer cpfseq FSR0H ;and compare with end pointer bra PutRxBuf1 ;skip low bytes if high bytes not equal movlw LOW (RxBuffer+RX_BUF_LEN) ;get last address of buffer cpfseq FSR0L ;and compare with end pointer bra PutRxBuf1 ;go save new pointer if not at end lfsr 0,RxBuffer ;point to beginning of buffer if at end PutRxBuf1: movff FSR0H,RxEndPtrH ;save new EndPointer high byte movff FSR0L,RxEndPtrL ;save new EndPointer low byte ;test if buffer is full movf RxStartPtrH,W ;get start pointer cpfseq RxEndPtrH ;and compare with end pointer bra PutRxBuf2 ;skip low bytes if high bytes not equal movf RxStartPtrL,W ;get start pointer cpfseq RxEndPtrL ;and compare with end pointer bra PutRxBuf2 bsf Flags,RxBufFull ;if same then indicate buffer full PutRxBuf2: bcf Flags,RxBufEmpty ;Rx buffer cannot be empty return ;error because attempting to store new data and the buffer is full ;can do special error handling here - this code simply ignores the byte ErrRxBufFull: return ;no save of data because buffer is full ;---------------------------------------------- ;Remove and return (in WREG) the byte at the start of the transmit buffer. GetTxBuffer: btfsc Flags,TxBufEmpty ;check if transmit buffer empty bra ErrTxBufEmpty ;go handle error if empty movff TxStartPtrH,FSR0H ;put StartPointer into FSR0 movff TxStartPtrL,FSR0L ;put StartPointer into FSR0 movff POSTINC0,TempTxData ;save data from buffer ;test if buffer pointer needs to wrap around to beginning of buffer memory movlw HIGH (TxBuffer+TX_BUF_LEN) ;get last address of buffer cpfseq FSR0H ;and compare with start pointer bra GetTxBuf1 ;skip low bytes if high bytes not equal movlw LOW (TxBuffer+TX_BUF_LEN) ;get last address of buffer cpfseq FSR0L ;and compare with start pointer bra GetTxBuf1 ;go save new pointer if at end lfsr 0,TxBuffer ;point to beginning of buffer if at end GetTxBuf1: movff FSR0H,TxStartPtrH ;save new StartPointer value movff FSR0L,TxStartPtrL ;save new StartPointer value ;test if buffer is now empty movf TxEndPtrH,W ;get end pointer cpfseq TxStartPtrH ;and compare with start pointer bra GetTxBuf2 ;skip low bytes if high bytes not equal movf TxEndPtrL,W ;get end pointer cpfseq TxStartPtrL ;and compare with start pointer bra GetTxBuf2 bsf Flags,TxBufEmpty ;if same then indicate buffer empty GetTxBuf2: bcf Flags,TxBufFull ;Tx buffer cannot be full movf TempTxData,W ;restore data from buffer return ;error because attempting to read data from an empty buffer ;can do special error handling here - this code simply returns zero ErrTxBufEmpty: retlw 0 ;buffer empty, return zero ;---------------------------------------------- ;Remove and return (in WREG) the byte at the start of the receive buffer. GetRxBuffer: bcf PIE1,RCIE ;disable Rx interrupt to avoid conflict btfsc Flags,RxBufEmpty ;check if receive buffer empty bra ErrRxBufEmpty ;go handle error if empty movff RxStartPtrH,FSR0H ;put StartPointer into FSR0 movff RxStartPtrL,FSR0L ;put StartPointer into FSR0 movff POSTINC0,TempRxData ;save data from buffer ;test if buffer pointer needs to wrap around to beginning of buffer memory movlw HIGH (RxBuffer+RX_BUF_LEN) ;get last address of buffer cpfseq FSR0H ;and compare with start pointer bra GetRxBuf1 ;skip low bytes if high bytes not equal movlw LOW (RxBuffer+RX_BUF_LEN) ;get last address of buffer cpfseq FSR0L ;and compare with start pointer bra GetRxBuf1 ;go save new pointer if at end lfsr 0,RxBuffer ;point to beginning of buffer if at end GetRxBuf1: movff FSR0H,RxStartPtrH ;save new StartPointer value movff FSR0L,RxStartPtrL ;save new StartPointer value ;test if buffer is now empty movf RxEndPtrH,W ;get end pointer cpfseq RxStartPtrH ;and compare with start pointer bra GetRxBuf2 ;skip low bytes if high bytes not equal movf RxEndPtrL,W ;get end pointer cpfseq RxStartPtrL ; and compare with start pointer bra GetRxBuf2 bsf Flags,RxBufEmpty ;if same then indicate buffer empty GetRxBuf2: bcf Flags,RxBufFull ;Rx buffer cannot be full movf TempRxData,W ;restore data from buffer bsf PIE1,RCIE ;enable receive interrupt return ;error because attempting to read data from an empty buffer ;can do special error handling here - this code simply returns zero ErrRxBufEmpty: bsf PIE1,RCIE ;enable receive interrupt retlw 0 ;buffer empty, return zero ;---------------------------------------------------------------------------- ;Move data from receive buffer to transmit buffer to echo the line back. CopyRxToTx: ; bcf Flags,ReceivedCR ;clear received indicator Copy1: btfsc Flags,RxBufEmpty ;check if Rx buffer is empty return ;if so then return rcall GetRxBuffer ;get data from receive buffer ; movwf TempData ;save data rcall PutTxBuffer ;put data in transmit buffer ; movf TempData,W ;restore data ; xorlw 0x0d ;compare with ; bnz Copy1 ;if not the same then move another byte bra Copy1 return ;---------------------------------------------------------------------------- ; Timer3 setup SetupTimer3: movlw 0x01 movwf T3CON ; timer3 on, other bits cleared bsf IPR2,1 ; timer3 = high priority interrupt bsf PIE2,1 ; timer3 interrupt enable return Timer3ISR: bcf PIR2,TMR3IF ; clear interrupt flag movlw 050h ;0x7F; 0xA0 movwf TMR3L ; preset timer setf TMR3H ; preset timer #ifdef heartbeat bsf LATC,0; ; LATC:5 = TIMER ISR HEARTBEAT #endif ; bsf LATC,0; ; heatbeat movf OutsA,0 movwf LATA; movf OutsB,0 movwf LATB; movf OutsC,0 #ifndef heartbeat movwf LATC; #endif lfsr FSR0 ,0000h lfsr FSR1 ,0000h clrf TBLPTRU ; address of the word ReadBuffer movf CmdBuff0RdIndx, W; subwf CmdBuff0WrIndx, W ; result in w bz Timers; no new task on cmd queue movff CmdBuff0RdIndx, FSR0L; movlw 0x08; addwf CmdBuff0RdIndx; movf CmdBuff0RdIndx,0; bnz NoWrap; movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0RdIndx; NoWrap movf POSTINC0, W; W = timer# addlw 010h; 10h= half of startaddress of timers addwf WREG; W = (0x10+timer# )* 2 movwf FSR1L; clrf FSR1H; FSR1 points to timer values movf POSTINC0, W; W = timer val msb movwf POSTINC1; movf POSTINC0, W; W = timer val lsb movwf POSTINC1; movf POSTINC0, W; W = timer output mask port A iorwf OutsA; set output w/ mask movf POSTINC0, W; W = timer output mask port B iorwf OutsB; set output w/ mask movf POSTINC0, W; W = timer output mask port C iorwf OutsC; set output w/ mask ; movf INDF0, W; W = velocity ; MIDI velocity * 2 ; addwf INDF0, W; double ; MOVWF TBLPTRL ; TBLRD*+ ; read into TABLAT and increment ; MOVF TABLAT, W ; get data ; MOVWF PREINC1 ; strange endianess ; MOVWF POSTDEC1 ; charge timer ; TBLRD* ; read into TABLAT and do not increment (wraps...!) ; MOVF TABLAT, W ; get data ; MOVWF INDF1 ; charge timer ; Set outputs with mask from velocity table ; clrf TBLPTRL ; ; tblrd*+ ; read into TABLAT and increment ; movf TABLAT, W ; get data ; iorwf OutsA; set output w/ mask ; tblrd*+ ; read into TABLAT and increment ; movf TABLAT, W ; set output w/ mask ; iorwf OutsB; ;; IORWF ??? ;************************************************************ ; Timers ; !!! change to timer interrupt instead of idle loop !!! Timers Timer0 decf Timer0Lsb; bc Timer1 ; decf Timer0Msb; bc Timer1 ; setf Timer0Lsb; tricky way to reduce avg -> 2.5 cycles worstcase 7 cycles ; clrf Timer0Msb; ; bcf OutsA, 2; Timer0PinClr ; clrf Timer0Out; Timer1 decf Timer1Lsb; bc Timer2 ; decf Timer1Msb; bc Timer2 ; setf Timer1Lsb; Timer1PinClr Timer2 decf Timer2Lsb; bc Timer3 ; decf Timer2Msb; bc Timer3 ; setf Timer2Lsb; Timer2PinClr Timer3 decf Timer3Lsb; bc Timer4 ; decf Timer3Msb; bc Timer4 ; setf Timer3Lsb; Timer3PinClr Timer4 decf Timer4Lsb; bc Timer5 ; decf Timer4Msb; bc Timer5 ; setf Timer4Lsb; Timer4PinClr Timer5 decf Timer5Lsb; bc Timer6 ; decf Timer5Msb; bc Timer6 ; setf Timer5Lsb; Timer5PinClr Timer6 decf Timer6Lsb; bc Timer7 ; decf Timer6Msb; bc Timer7 ; setf Timer6Lsb; Timer6PinClr Timer7 decf Timer7Lsb; bc Timer8 ; decf Timer7Msb; bc Timer8 ; setf Timer7Lsb; Timer7PinClr Timer8 decf Timer8Lsb; bc Timer9 ; decf Timer8Msb; bc Timer9 ; setf Timer8Lsb; Timer8PinClr Timer9 decf Timer9Lsb; bc Timer10 ; decf Timer9Msb; bc Timer10 ; setf Timer9Lsb; Timer9PinClr Timer10 decf Timer10Lsb; bc Timer11 ; decf Timer10Msb; bc Timer11 ; setf Timer10Lsb; Timer10PinClr Timer11 decf Timer11Lsb; bc Timer12 ; decf Timer11Msb; bc Timer12 ; setf Timer11Lsb; Timer11PinClr Timer12 decf Timer12Lsb; bc Timer13 ; decf Timer12Msb; bc Timer13 ; setf Timer12Lsb; Timer12PinClr Timer13 decf Timer13Lsb; bc Timer14 ; decf Timer13Msb; bc Timer14 ; setf Timer13Lsb; Timer13PinClr Timer14 decf Timer14Lsb; bc Timer15 ; decf Timer14Msb; bc Timer15 ; setf Timer14Lsb; Timer14PinClr Timer15 decf Timer15Lsb; bc Timer16 ; decf Timer15Msb; bc Timer16 ; setf Timer15Lsb; Timer15PinClr Timer16 TimerOut ;;; ; movf Timer3Lsb,w ; movlw 0FFh ; ; movwf PORTB ;;; ; bcf LATC,5; ; LATC:5 = TIMER ISR HEARTBEAT #ifdef heartbeat bcf LATC,0; ; heatbeat #endif #ifdef simulate return ; OUTSIDE ISR *WARNING* FOR SIMULATION ONLY #endif bra EndHighInt ; resume ;---------------------------------------------------------------------------- ; MIDI Input parser ; ; pseudocode: ; ; MidiInParser: ; if (inByte & 0x80) ; MidiStatus = inByte ; goto MidiInStatus ; else ; goto MidiInData ; return ; ; MidiInStatus: ; Look up # databytes ; 1000xxxx -> 2 (noteOn) ; 1001xxxx -> 2 (noteOff) ; 1010xxxx -> 2 (polyKeyPr) ; 1011xxxx -> 2 (ctrlChange) ; 1100xxxx -> 1 (pgmChg ; 1101xxxx -> 1 (chnlPr) ; 1110xxxx -> 0 (System Common Messages -> ignore) ; 1111xxxx -> 0 (SysEx) ; ; store in MidiNumData ; MidiCurDataBytes=0 ; return ; ; MidiInData: ; If (CurDBytes==0) ; MidiData1=inByte ; If (CurDBytes==1) ; MidiData2=inByte ; CurDBytes++; ; ; If (CurDBytes>MidiNumData) ; If (MidiStatus==NoteOn) ; If (channel = x) ; ... ; ... ; ; If (MidiStatus==NoteOff) ; If (channel = x) ; ... ; ; goto MidiInStatus PollMidiIn: btfsc Flags,RxBufEmpty ;check if Rx buffer is empty return ;if so then return rcall GetRxBuffer ;get data from receive buffer MidiInParser: movwf MidiInByte; btfss MidiInByte,7; bra MidiInData; ; jump if it wasn't a status byte movwf MidiByte0; MidiInStatus: movlw 0; clrf MidiCurData; btfss MidiByte0,6; bra MidiInStatus2words; btfss MidiByte0,5; movlw 1; movwf MidiNumData; bra MidiInStatusEnd; MidiInStatus2words: movlw 2; movwf MidiNumData; MidiInStatusEnd: return MidiInData: tstfsz MidiCurData; bra MidiInDataM2; bra MidiInDataM1; MidiInDataM2: ; 2nd byte movff MidiInByte,MidiByte2; clrf MidiCurData; bra MidiInAction2Bytes; MidiInDataM1: movff MidiInByte,MidiByte1; incf MidiCurData,1; movf MidiCurData,0; subwf MidiNumData,0; bz MidiInAction1Byte; return; MidiInAction1Byte: return; MidiInAction2Bytes: movlw 09Dh; noteOn, ch0 subwf MidiByte0,0; bz MidiInNoteOn; movlw 08Dh; noteOff, ch0 subwf MidiByte0,0; bz MidiInNoteOff; return; MidiInNoteOn: ;************** ; lookup timer #, read from table in PM:0Fvv movf MidiByte2,0; bz MidiInNoteOff; ; velocity=0 -> NOTE OFF movlw 0Fh; movwf TBLPTRH MOVFF MidiByte1,TBLPTRL TBLRD* ; read into TABLAT movf TABLAT,0 bz MidiInNoteOn2 ; decf TABLAT,0 movwf TmpT ; TmpT = timer# ; add to cmd queue ; btg LATC,2; ; toggle LATC:2 = Note On HEARTBEAT lfsr FSR2 ,0000h movff CmdBuff0WrIndx, FSR2L; ; movf TABLAT,0 movwf POSTINC2,0 ; write timer # ; lookup velo -> timer val movf MidiByte2,0; addwf MidiByte2,0; movwf TBLPTRL; movf TmpT,0; addlw 010h; movwf TBLPTRH TBLRD*+ ; movff TABLAT,TmpM TBLRD*+ ; movf TABLAT,0 movwf POSTINC2,0 ; write timer lsb movf TmpM,0; movwf POSTINC2,0 ; write timer msb movlw 0Eh; movwf TBLPTRH movf TmpT,0; addwf TmpT,0; addwf TmpT,0; addwf TmpT,0; movwf TBLPTRL; TBLPTRL = 4x timer# TBLRD*+ ; movf TABLAT,0; movwf POSTINC2,0 ; write timer pin mask A TBLRD*+ ; movf TABLAT,0; movwf POSTINC2,0 ; write timer pin mask B TBLRD*+ ; movf TABLAT,0; movwf POSTINC2,0 ; write timer pin mask C movwf POSTINC2,0 ; write dummy to increment movwf POSTINC2,0 ; write dummy to increment movf FSR2L,0; bz NoWrap2; corrected: was: bnz movff FSR2L, CmdBuff0WrIndx; ; btg LATC,1; MIDI rcv hearbeat return NoWrap2 movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0WrIndx; return ;************************************************************ ; Timer # to three-byte pin mask (A, B, C) ; ### or can it better be done directly in a function ? ; ### Where is this pinamsk used? ; ### cycle-budget: a. lookup ; ### 4 for lookup, 6 for applying, makes 10 cycles ; ### b. function: ; ;************************************************************ ; Midi Note number to timer number table (offset by 1!) ; 01h -> no action ; org 0F00h ; ; ;************************************************************