; 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 ;============================================================================= ; Filename: midima.asm ;============================================================================= ; Author: Johannes Taelman ; Company: Johannes Taelman ; Revision: Godfried-Willem Raes 04.07.2007 ; Date: May 2005 ;============================================================================= ; Include Files: p18f2525.inc V1.3 ;============================================================================= list p=18f2525 ;list directive to define processor #include ;processor specific definitions ; #include ;processor specific definitions ; #define simulate 1 ; enable simulation ; #define heartbeat 1 ; enable heatbeats ;****************************************************************************** ;Configuration bits ; The __CONFIG directive defines configuration data within the .ASM file. ; The labels following the directive are defined in the P18F2525.INC file. ; The PIC18FXX2 Data Sheet explains the functions of the configuration bits. ; Change the following lines to suit your application. __CONFIG _CONFIG1H, _OSCS_OFF_1H & _HSPLL_OSC_1H __CONFIG _CONFIG2L, _BOR_ON_2L & _PWRT_ON_2L __CONFIG _CONFIG2H, _WDT_OFF_2H __CONFIG _CONFIG3H, _CCP2MX_OFF_3H __CONFIG _CONFIG4L, _STVR_OFF_4L & _LVP_OFF_4L & _DEBUG_OFF_4L __CONFIG _CONFIG5L, _CP0_OFF_5L & _CP1_OFF_5L & _CP2_OFF_5L & _CP3_OFF_5L __CONFIG _CONFIG5H, _CPB_OFF_5H & _CPD_OFF_5H __CONFIG _CONFIG6L, _WRT0_OFF_6L & _WRT1_OFF_6L & _WRT2_OFF_6L & _WRT3_OFF_6L __CONFIG _CONFIG6H, _WRTC_OFF_6H & _WRTB_OFF_6H & _WRTD_OFF_6H __CONFIG _CONFIG7L, _EBTR0_OFF_7L & _EBTR1_OFF_7L & _EBTR2_OFF_7L & _EBTR3_OFF_7L __CONFIG _CONFIG7H, _EBTRB_OFF_7H #define Timer1Out OutsA,1; #define Timer2Out OutsA,3; #define Timer3Out OutsA,5; #define Timer4Out OutsB,6; #define Timer5Out OutsB,4; #define Timer6Out OutsB,2; #define Timer7Out OutsB,0; #define Timer8Out OutsC,4; #define Timer9Out OutsC,1; #define Timer10Out OutsC,3; #define Note1Hold OutsA,2; #define Note2Hold OutsA,0; #define Note3Hold OutsA,4; #define Note4Hold OutsB,7; #define Note5Hold OutsB,5; #define Note6Hold OutsB,3; #define Note7Hold OutsB,1; #define Note8Hold OutsC,5; #define Note9Hold OutsC,0; #define Note10Hold OutsC,2; ;---------------------------------------------------------------------------- ;Constants SPBRG_VAL EQU 04Fh ;set baud rate 31250 for 40Mhz clock TX_BUF_LEN EQU 010h ;length of transmit circular buffer RX_BUF_LEN EQU TX_BUF_LEN ;length of receive circular buffer ;---------------------------------------------------------------------------- ;Bit Definitions TxBufFull EQU 0 ;bit indicates Tx buffer is full TxBufEmpty EQU 1 ;bit indicates Tx buffer is empty RxBufFull EQU 2 ;bit indicates Rx buffer is full RxBufEmpty EQU 3 ;bit indicates Rx buffer is empty ;---------------------------------------------------------------------------- ; Variables ; memory map ;---------------------------------------------------------------------------- CBLOCK 0x000 WREG_TEMP ;to save WREG during interrupt STATUS_TEMP ;to save STATUS during interrupt BSR_TEMP ;to save BSR during interrupt FSR0H_TEMP ;to save FSR0H during interrupt FSR0L_TEMP ;to save FSR0L during interrupt FSR0H_SHADOW ;to save FSR0H during high interrupt FSR0L_SHADOW ;to save FSR0L during high interrupt MidiInByte ; Rcv'd Byte MidiByte0 ; status MidiByte1 ; data1 MidiByte2 ; data2 MidiCurData ; current data byte index MidiNumData ; expected data bytes MidiTestByte ; for testing only TmpM ; non-ISR scratch TmpT ; non-ISR scratch OutsA ; output port A internal states OutsB ; output port B internal states OutsC ; output port C internal states ENDC CBLOCK 0x020 ; DO NOT MOVE THIS BLOCK Timer1Msb Timer1Lsb Timer2Msb Timer2Lsb Timer3Msb Timer3Lsb Timer4Msb Timer4Lsb Timer5Msb Timer5Lsb Timer6Msb Timer6Lsb Timer7Msb Timer7Lsb Timer8Msb Timer8Lsb Timer9Msb Timer9Lsb Timer10Msb Timer10Lsb ENDC CBLOCK 0x40 ; Flags ;byte for indicator flag bits TempData ;temporary data in main routines TempRxData ;temporary data in Rx buffer routines TempTxData ;temporary data in Tx buffer routines TxStartPtrH ;pointer to start of data in Tx buffer TxStartPtrL ;pointer to start of data in Tx buffer TxEndPtrH ;pointer to end of data in Tx buffer TxEndPtrL ;pointer to end of data in Tx buffer RxStartPtrH ;pointer to start of data in Rx buffer RxStartPtrL ;pointer to start of data in Rx buffer RxEndPtrH ;pointer to end of data in Rx buffer RxEndPtrL ;pointer to end of data in Rx buffer TxBuffer:TX_BUF_LEN ;Tx buffer for data to transmit RxBuffer:RX_BUF_LEN ;Rx buffer for received data ENDC ; DO NOT MOVE THIS BLOCK CmdBuff0RdIndx equ 0EEh CmdBuff0WrIndx equ 0EFh CmdBuff0Timer equ 0F4h CmdBuff0TimerValMSB equ 0F5h CmdBuff0TimerValLSB equ 0F6h CmdBuff1Timer equ 0F7h CmdBuff1TimerValMSB equ 0F8h CmdBuff1TimerValLSB equ 0F9h CmdBuff2Timer equ 0FAh CmdBuff2TimerValMSB equ 0FBh CmdBuff2TimerValLSB equ 0FCh CmdBuff3Timer equ 0FDh CmdBuff3TimerValMSB equ 0FEh CmdBuff3TimerValLSB equ 0FFh ; ... and incrementing this last adress wraps it to zero. ; don't change this property! ;---------------------------------------------------------------------------- ; end of memory map ;---------------------------------------------------------------------------- ;---------------------------------------------------------------------------- ;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: 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 movf RCREG,W ;get received 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 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 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 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 #ifdef ICD movlw 0xE0 movwf TRISB #else clrf TRISB ; Config PORTB as all outputs #endif movlw 0x80 ; movwf TRISC ; Config PORTC all outputs + tristate for serial rx movlw 0x07 ; init adc movwf ADCON1 ; init adc clrf ADCON0 ; poweroff adc clrf OutsA; clrf OutsB; clrf OutsC; bcf LATC,6 ; 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 bsf LATC,6 ; 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 movlw 0x90; movwf MidiByte0 movlw 0x0F; movwf MidiByte1 movlw 0x40; movwf MidiByte2 ;testloop: ; nop ; call MidiInNoteOn ; bra testloop MainLoop: #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: Copy1: btfsc Flags,RxBufEmpty ;check if Rx buffer is empty return ;if so then return rcall GetRxBuffer ;get data from receive buffer rcall PutTxBuffer ;put data in transmit buffer 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 090h ;0x7F; 0xA0 movwf TMR3L ; preset timer setf TMR3H ; preset timer movf OutsA,0 movwf LATA; movf OutsB,0 movwf LATB; movf OutsC,0 movwf LATC; ReadBuffer movf CmdBuff0RdIndx, W; subwf CmdBuff0WrIndx, W ; result in w bz Timers; no new task on cmd queue movff CmdBuff0RdIndx, FSR0L; clrf FSR0H movlw 0x03; addwf CmdBuff0RdIndx bnz NoWrap; movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0RdIndx; NoWrap movf INDF0,W; call SetPulseAndHold 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; ;************************************************************ ; Timers ; !!! change to timer interrupt instead of idle loop !!! Timers Timer1 decf Timer1Lsb; bc Timer2 ; decf Timer1Msb; bc Timer2 ; setf Timer1Lsb; bcf Timer1Out Timer2 decf Timer2Lsb; bc Timer3 ; decf Timer2Msb; bc Timer3 ; setf Timer2Lsb; bcf Timer2Out Timer3 decf Timer3Lsb; bc Timer4 ; decf Timer3Msb; bc Timer4 ; setf Timer3Lsb; bcf Timer3Out Timer4 decf Timer4Lsb; bc Timer5 ; decf Timer4Msb; bc Timer5 ; setf Timer4Lsb; bcf Timer4Out Timer5 decf Timer5Lsb; bc Timer6 ; decf Timer5Msb; bc Timer6 ; setf Timer5Lsb; bcf Timer5Out Timer6 decf Timer6Lsb; bc Timer7 ; decf Timer6Msb; bc Timer7 ; setf Timer6Lsb; bcf Timer6Out Timer7 decf Timer7Lsb; bc Timer8 ; decf Timer7Msb; bc Timer8 ; setf Timer7Lsb; bcf Timer7Out Timer8 decf Timer8Lsb; bc Timer9 ; decf Timer8Msb; bc Timer9 ; setf Timer8Lsb; bcf Timer8Out Timer9 decf Timer9Lsb; bc Timer10 ; decf Timer9Msb; bc Timer10 ; setf Timer9Lsb; bcf Timer9Out Timer10 decf Timer10Lsb; bc Timer11 ; decf Timer10Msb; bc Timer11 ; setf Timer10Lsb; bcf Timer10Out Timer11 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 (MidiCurData==0) ; MidiData1=inByte ; If (MidiCurData==1) ; MidiData2=inByte ; MidiCurData++; ; ; If (MidiCurData==MidiNumData) ; { ; If (MidiStatus==NoteOn + channel) ; ... ; ; If (MidiStatus==NoteOff + channel) ; } ... ; 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; rcall PutTxBuffer; ; return movf MidiInByte,W; btfss MidiInByte,7; bra MidiInData; ; jump if it wasn't a status byte movwf MidiByte0; MidiInStatus: clrf MidiCurData; btfss MidiByte0,6; bra MidiInStatus2words; btfss MidiByte0,5; movlw 1; movwf MidiNumData; return 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; movf MidiCurData,W; subwf MidiNumData,W; bz MidiInAction1Byte; return; MidiInAction1Byte: return; MidiInAction2Bytes: movlw 090h + MidiChannel; noteOn subwf MidiByte0,0; bz MidiInNoteOn; movlw 080h + MidiChannel; noteOff subwf MidiByte0,0; bz MidiInNoteOff; movlw 0B0h + MidiChannel; CtrlChange subwf MidiByte0,0; bz MidiInCtrlChange; return; MidiInNoteOn: ; lookup timer #, read from table in PM:0Fvv movf MidiByte2,W; bz MidiInNoteOffNoVelo; ; velocity=0 -> NOTE OFF ; check if note is in range movlw FirstNote subwf MidiByte1,W bn MidiInNoteOn2 ; movwf TmpT movlw LastNote subwf MidiByte1,W bnn MidiInNoteOn2 ; ; add to cmd queue clrf FSR0H movff CmdBuff0WrIndx, FSR0L; movf TmpT,W movwf POSTINC0; write timer # ; lookup velo -> timer val movf MidiByte2,W; addwf MidiByte2,W; movwf TBLPTRL; movlw 010h; movwf TBLPTRH TBLRD*+ ; movff TABLAT,TmpM TBLRD*+ ; movf TABLAT,W movwf POSTINC0 ; write timer lsb movf TmpM,W; movwf POSTINC0 ; write timer msb movf FSR0L,W; bz NoWrap2; corrected: was: bnz movwf CmdBuff0WrIndx; return NoWrap2 movlw CmdBuff0Timer; Wrap to start movwf CmdBuff0WrIndx; return MidiInNoteOn2: return MidiInNoteOffNoVelo: MidiInNoteOff: ; for non-timered note outputs movlw FirstNote subwf MidiByte1,W bn MidiInNoteOff2; movwf TmpT movlw LastNote subwf MidiByte1,W bnn MidiInNoteOff2; movf TmpT,W bra ClearHold return MidiInNoteOff2: return MidiInCtrlChange: movlw 0x7B; AllNotesOff subwf MidiByte1,0; bz MidiInAllNotesOff; return MidiInAllNotesOff: clrf OutsA; clrf OutsB; clrf OutsC; clrf LATA clrf LATB clrf LATC return ORG 0x0500 SetPulseAndHold: ; lookup using a computed goto ; cfr. PIC18fxx2 datasheet section 4.4 mullw 0x06; movf PCL,W movf PRODL,W addwf PCL _sph1: bsf Note1Hold bsf Timer1Out return _sph2: bsf Note2Hold bsf Timer2Out return _sph3: bsf Note3Hold bsf Timer3Out return _sph4: bsf Note4Hold bsf Timer4Out return _sph5: bsf Note5Hold bsf Timer5Out return _sph6: bsf Note6Hold bsf Timer6Out return _sph7: bsf Note7Hold bsf Timer7Out return _sph8: bsf Note8Hold bsf Timer8Out return _sph9: bsf Note9Hold bsf Timer9Out return _sph10: bsf Note10Hold bsf Timer10Out return ClearHold: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _ch1: bcf Note1Hold return _ch2: bcf Note2Hold return _ch3: bcf Note3Hold return _ch4: bcf Note4Hold return _ch5: bcf Note5Hold return _ch6: bcf Note6Hold return _ch7: bcf Note7Hold return _ch8: bcf Note8Hold return _ch9: bcf Note9Hold return _ch10: bcf Note10Hold return SetHold: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _sh1: bsf Note1Hold return _sh2: bsf Note2Hold return _sh3: bsf Note3Hold return _sh4: bsf Note4Hold return _sh5: bsf Note5Hold return _sh6: bsf Note6Hold return _sh7: bsf Note7Hold return _sh8: bsf Note8Hold return _sh9: bsf Note9Hold return _sh10: bsf Note10Hold return SetPulse: mullw 0x04; movf PCL,W movf PRODL,W addwf PCL _sp1 bsf Timer1Out return _sp2 bcf Timer2Out return _sp3 bcf Timer3Out return _sp4 bcf Timer4Out return _sp5 bcf Timer5Out return _sp6 bcf Timer6Out return _sp7 bcf Timer7Out return _sp8 bcf Timer8Out return _sp9 bcf Timer9Out return _sp10 bcf Timer10Out return