;;; 80 characters wide please ;;;;;;;;;;;;;;;;;;;;;;;;;; 8-space tabs please ;;; ; ;;; ;;;;; TashKM: Daisy-Chain-Controlled ADB Device ;;; ; ;;; License ;;; ; This program is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 3 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program. If not, see . ;;; Connections ;;; ;;; ;;; ; .--------. ; ; Supply -|01 \/ 08|- Ground ; ; ADB Data <--> RA5 -|02 07|- RA0/TX ---> To Next RX ; ; ADB Power <--- RA4 -|03 06|- RA1/RX <--- From Previous TX ; ; ---> RA3 -|04 05|- RA2 ---- ; ; '--------' ; ;;; ;;; ;;; Assembler Directives ;;; list P=PIC12F1840, F=INHX32, ST=OFF, MM=OFF, R=DEC, X=ON #include P12F1840.inc __config _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF ;_FOSC_INTOSC Internal oscillator, I/O on RA5 ;_WDTE_OFF Watchdog timer disabled ;_PWRTE_ON Keep in reset for 64 ms on start ;_MCLRE_OFF RA3/!MCLR is RA3 ;_CP_OFF Code protection off ;_CPD_OFF Data memory protection off ;_BOREN_OFF Brownout reset off ;_CLKOUTEN_OFF CLKOUT disabled, I/O on RA4 ;_IESO_OFF Internal/External switch not needed ;_FCMEN_OFF Fail-safe clock monitor not needed __config _CONFIG2, _WRT_OFF & _PLLEN_ON & _STVREN_ON & _LVP_OFF ;_WRT_OFF Write protection off ;_PLLEN_ON 4x PLL on ;_STVREN_ON Stack over/underflow causes reset ;_LVP_OFF High-voltage on Vpp to program ;;; Macros ;;; DELAY macro value ;Delay 3*W cycles, set W to 0 movlw value decfsz WREG,F bra $-1 endm DNOP macro bra $+1 endm ;;; Constants ;;; ;Timer0 values to time out after: PULSE_DNS equ -8 ;A short down pulse (first half of a '1') PULSE_DNL equ -16 ;A long down pulse (first half of a '0') PULSE_UPS equ -8 ;A short up pulse (second half of a '0') PULSE_UPL equ -16 ;A long up pulse (second half of a '1') ;RX_FLAGS: SRQ_ON equ 7 ;Set when holding the line low for a service request RX_RST equ 6 ;Set when the receiver has detected a reset condition RX_CMD equ 5 ;Set when ADB_CMD contains a command byte RX_DATA equ 4 ;Set when ADB_BUF-ADB_BUF8 contain a data packet RX_ABRT equ 3 ;Set when a data packet has been aborted RX_SIZ2 equ 2 ;Number of bytes received minus one RX_SIZ1 equ 1 ; " RX_SIZ0 equ 0 ; " ;TX_FLAGS: TX_RDY equ 7 ;Set when data is set up to transmit TX_ON equ 6 ;Set when transmitter is running TX_COL equ 5 ;Set when transmitter detected a collision TX_SIZ2 equ 2 ;Number of bytes to be transmitted minus one TX_SIZ1 equ 1 ; " TX_SIZ0 equ 0 ; " ;DV_FLAGS: DV_KBDC equ 7 ;Set when there was a collision on keyboard's address DV_MSEC equ 6 ;Set when there was a collision on mouse's address DV_MSES equ 4 ;Set when the mouse needs service ;U_FLAGS: U_HEADR equ 7 ;Set when we've received and are working a header byte U_FWD equ 6 ;Set when we're just forwarding (not queuing) bytes U_ISMSE equ 5 ;Set to receive for the mouse, clear for keyboard U_CNTR3 equ 3 ;Counter of bytes to forward or act on U_CNTR2 equ 2 ; " U_CNTR1 equ 1 ; " U_CNTR0 equ 0 ; " ;KBD_2_H: K2H_DEL equ 6 ;Delete K2H_CAP equ 5 ;CapsLock K2H_RST equ 4 ;Reset K2H_CTL equ 3 ;Control K2H_SHF equ 2 ;Shift K2H_OPT equ 1 ;Option K2H_CMD equ 0 ;Command ;KBD_2_L: K2L_CLR equ 7 ;Clear K2L_SLK equ 6 ;ScrollLock ;KBD_MODS: KMD_LCT equ 7 ;Left Control KMD_RCT equ 6 ;Right Control KMD_LSH equ 5 ;Left Shift KMD_RSH equ 4 ;Right Shift KMD_LOP equ 3 ;Left Option KMD_ROP equ 2 ;Right Option ;;; Variable Storage ;;; cblock 0x70 ;Bank-common registers RX_FLAGS ;Receiver flags TX_FLAGS ;Transmitter flags DV_FLAGS ;Device flags U_FLAGS ;UART flags RX_TDOWN ;Time spent down in the last down-up transition RX_TUP ;Time spent up in the last down-up transition ADB_PTR ;Receiver/transmitter state machine pointer ADB_CMD ;Receiver command buffer ADB_BUF ;Receiver/transmitter buffer ADB_BUF2 ;2nd byte of receiver/transmitter buffer ADB_BUF3 ;3rd byte of receiver/transmitter buffer ADB_BUF4 ;4th byte of receiver/transmitter buffer ADB_BUF5 ;5th byte of receiver/transmitter buffer ADB_BUF6 ;6th byte of receiver/transmitter buffer ADB_BUF7 ;7th byte of receiver/transmitter buffer ADB_BUF8 ;8th byte of receiver/transmitter buffer endc cblock 0xD0 ;Upper half of bank 1 registers KBD_2_H ;Keyboard register 2 high byte KBD_2_L ;Keyboard register 2 low byte KBD_MODS ;Keyboard modifier key state KBD_3_H ;Keyboard register 3 high byte KBD_3_L ;Keyboard register 3 low byte MSE_DXH ;Mouse delta X high byte MSE_DXL ;Mouse delta X low byte MSE_DYH ;Mouse delta Y high byte MSE_DYL ;Mouse delta Y low byte MSE_BTN ;Mouse buttons MSE_3_H ;Mouse register 3 high byte MSE_3_L ;Mouse register 3 low byte endc ;;; Vectors ;;; org 0x0 ;Reset vector bra Init org 0x4 ;Interrupt vector ;;; Interrupt Handler ;;; Interrupt btfsc RX_FLAGS,SRQ_ON ;If the SRQ_ON flag is set, branch into the bra IntSrqDone ; logic to end the service request condition btfsc TX_FLAGS,TX_ON ;If the TX_ON flag is set, branch into the bra IntXmit ; transmitter logic ;fall through IntRecv movlb 0 ;Quick as we can, capture the current value movf TMR0,W ; from Timer0 and reset it clrf TMR0 ; " btfsc INTCON,TMR0IF ;If Timer0 overflowed, set the captured value movlw 0xFF ; to 0xFF instead movlb 7 ;If we aren't here because of an on-change btfss IOCAF,5 ; interrupt, we're here because of a timer bra IntRecvTimeout ; overflow while the bus is high - a timeout btfss IOCAN,5 ;If the on-change interrupt is set to capture bra IntRecvRising ; rising edges, branch into rising-edge logic ;fall through IntRecvFalling bcf IOCAN,5 ;Switch the on-change interrupt to capture bsf IOCAP,5 ; rising edges bcf IOCAF,5 ;Clear the on-change interrupt flag bcf INTCON,TMR0IF ;Reset the Timer0 interrupt if it was set bcf INTCON,TMR0IE ;Don't interrupt on bus timeout when low addlw 33 ;Adjust the time the line spent floating high movwf RX_TUP ; so it's zero-based and store it addwf RX_TDOWN,W ;Check if the sum of the time spent up and the btfsc STATUS,C ; time spent down is greater than 132 us; if it bra IntRecvNotBit ; is, then it's out of range for a 1 or 0 bit addlw -34 ; cell and must be something else btfsc STATUS,C ; " bra IntRecvNotBit ; " ;fall through IntRecvBitCell addlw 34 ;If it is 132 us or less, halve it to see what lsrf WREG,W ; the threshold is to tell a 1 from a 0 subwf RX_TDOWN,W ;If the down time is less than the threshold, movlp high RxFsa0 ; point PCLATH to the state machine for btfss STATUS,C ; receiving a 1, else a zero movlp high RxFsa1 ; " movf ADB_PTR,W ;Jump into the appropriate state machine callw ; " movwf ADB_PTR ; " retfie IntRecvNotBit movf RX_TDOWN,W ;Bucketize the time the line spent pulled down; movlp high RxFsaR ; if none of these apply, treat as a reset addlw -24 ;232 ;If <= 23 (70us +30%), treat as 0 (stop bit) btfss STATUS,C ; " movlp high RxFsa0 ; " addlw -75 ;181 ;If <= 98 (300us +30%), treat as service req btfss STATUS,C ; " movlp high RxFsaS ; " addlw -108 ;148 ;If <= 206 (800us +3%), treat as attention btfss STATUS,C ; " movlp high RxFsaA ; " movf ADB_PTR,W ;Jump into the appropriate state machine callw ; " movwf ADB_PTR ; " retfie IntRecvRising btfsc TX_FLAGS,TX_RDY ;If the mainline code is ready to transmit, bra IntXmitReady ; a rising edge is where we switch modes bcf IOCAP,5 ;Switch the on-change interrupt to capture bsf IOCAN,5 ; falling edges bcf IOCAF,5 ;Clear the on-change interrupt flag movwf RX_TDOWN ;Store the time the line spent pulled low bcf INTCON,TMR0IF ;Reset the Timer0 interrupt if it was set movlb 0 ;Set Timer0 to time out after 132 us, just movlw -33 ; a bit longer than the maximum bit cell time, movwf TMR0 ; so we catch the end of a transaction quickly bsf INTCON,TMR0IE ;Use Timer0 interrupt to detect bus timeout retfie IntRecvTimeout bcf INTCON,TMR0IE ;Only interrupt on bus timeout once movlp high RxFsaT ;Jump into the timeout state machine movf ADB_PTR,W ; " callw ; " movwf ADB_PTR ; " retfie IntSrqDone bcf RX_FLAGS,SRQ_ON ;Clear the SRQ_ON flag so we don't return here bcf INTCON,TMR0IF ;Clear the timer interrupt that brought us here bcf INTCON,TMR0IE ;Disable timer interrupt as the line is low movlb 1 ;Release the ADB line to end the service movlw B'00100000' ; request and cause an on-change interrupt iorwf TRISA,F ; " movlb 0 ;Set Timer0 to 75 (300 us), same nominal value movlw 75 ; as if the service request had been made by movwf TMR0 ; another device so receiver reads it as such retfie IntXmitReady bcf IOCAP,5 ;Switch on-change interrupt to capture falling bsf IOCAN,5 ; edges (trigger on other devices pulling low) bcf IOCAF,5 ;Clear the interrupt that brought us here bcf TX_FLAGS,TX_RDY ;Clear the TX_RDY flag and raise the TX_ON flag bsf TX_FLAGS,TX_ON ; to indicate that we've switched modes clrf ADB_PTR ;Clear state pointer now we've changed machines movlb 0 ;Get a pseudorandom number between 0 and 15, movf TMR1H,W ; turn it into a number between 199 and 214; xorwf TMR1L,W ; that will make Timer0 overflow in between andlw B'00001111' ; 168 us and 228 us, which is close enough to addlw -57 ; the specced range of 160 us to 240 us to wait movwf TMR0 ; before transmitting bcf INTCON,TMR0IF ;Reset the Timer0 interrupt if it was set bsf INTCON,TMR0IE ;Use Timer0 interrupt to cause transmit start retfie IntXmit btfsc INTCON,IOCIF ;If we're in the transmit logic because of IOC, bra IntXmitCol ; pin changed under us and we have a collision movlb 1 ;If not, we're here because of a timer movlw B'00100000' ; interrupt that signifies we should toggle xorwf TRISA,F ; whether we're pulling the ADB pin low btfss TRISA,5 ;If we're pulling the pin low, we can't detect bra IntXmitNoCol ; a collision, so assume there isn't one movlb 0 ;If we're letting the pin float but it's still btfsc PORTA,5 ; low, something else is driving it at the same bra IntXmitNoCol ; time as us and there's a collision ;fall through IntXmitCol bsf TX_FLAGS,TX_COL ;Raise the collision flag bcf TX_FLAGS,TX_ON ;Reset everything into receiver mode as though clrf ADB_PTR ; the line were idle; the receiver will ignore movlb 7 ; the rest of the conflicting transmission and movlw B'00100000' ; await the next attention or reset pulse movwf IOCAN ; " clrf IOCAP ; " clrf IOCAF ; " clrf RX_TDOWN ; " movlw B'00001000' ; " movwf INTCON ; " retfie IntXmitNoCol movlb 0 ;Call into the transmitter state machine movlp high TxFsa ; " movf ADB_PTR,W ; " callw ; " movwf ADB_PTR ; " bcf INTCON,TMR0IF ;Clear Timer0 interrupt that brought us here movlb 7 ;Clear the on-change interrupt in case we bcf IOCAF,5 ; pulled the line low and caused one retfie ;;; Mainline ;;; Init banksel OSCCON ;32 MHz (w/PLL) high-freq internal oscillator movlw B'11110000' movwf OSCCON banksel IOCAN ;RA5 sets IOCAF[5] on negative edge movlw B'00100000' movwf IOCAN banksel RCSTA ;UART async mode, 115.2 kHz movlw B'01001000' movwf BAUDCON clrf SPBRGH movlw 68 movwf SPBRGL movlw B'00100110' movwf TXSTA movlw B'10010000' movwf RCSTA banksel OPTION_REG ;Timer0 uses instruction clock, 1:32 prescaler, movlw B'01010100' ; thus ticking every 4 us; weak pull-ups on movwf OPTION_REG banksel T1CON ;Timer1 ticks with instruction clock movlw B'00000001' movwf T1CON banksel ANSELA ;All pins digital, not analog clrf ANSELA banksel LATA ;Ready to pull RA5-4 low when outputs movlw B'00001111' movwf LATA banksel TRISA ;TX output, RX and unused pins inputs, RA5-4 movlw B'00111110' ; open-collector outputs, currently off movwf TRISA movlb 1 movlw 0x22 ;Keyboard register 3 puts it at address 0x2, movwf KBD_3_H ; with SRQ enabled and handler ID 2 movlw 0x02 movwf KBD_3_L movlw 0x23 ;Mouse register 3 puts it at address 0x3, with movwf MSE_3_H ; SRQ enabled and handler ID 1 movlw 0x01 movwf MSE_3_L movlw 0x20 ;Set up keyboard queue (0x2000-0x207F), for movwf FSR0H ; which FSRs are push (FSR0) and pop (FSR1) movwf FSR1H ; pointers clrf FSR0L clrf FSR1L clrf TX_FLAGS ;Particularly important that these are zero clrf RX_FLAGS clrf DV_FLAGS clrf U_FLAGS clrf ADB_PTR movlw B'10001000' ;On-change interrupt and interrupt subsystem on movwf INTCON bra Main GotUartByte btfss U_FLAGS,U_HEADR ;If we're awaiting a header byte, this is it bra GUNewHeader ; " btfsc U_FLAGS,U_FWD ;If we're forwarding bytes, forward this one, bra GUForwardByte ; else queue it btfsc U_FLAGS,U_ISMSE ;If this byte is for the mouse, interpret it bra GUMouseByte ; as such, otherwise interpret for keyboard ;fall through GUKeyboardByte movlb 3 ;Push the byte that came in over the UART onto movf RCREG,W ; the keyboard queue movwi FSR0++ ; " bcf FSR0L,7 ; " clrf U_FLAGS ;Keyboard packet done, await new header bra Main GUMouseByte bcf DV_FLAGS,DV_MSES;Don't signal for service, data may not be done movf U_FLAGS,W ;Branch into one of the following depending on andlw B'00000011' ; the remaining-bytes counter to determine how brw ; to interpret this byte for the mouse: nop ;(0) This shouldn't happen, fall through to... bra GUMouseButtons ;(1) Finish with the mouse buttons bra GUMouseDeltaX ;(2) Add to the mouse X delta bra GUMouseDeltaY ;(3) Add to the mouse Y delta GUMouseButtons movlb 3 ;Grab the byte that came in over the UART movf RCREG,W ; " movlb 1 ;AND it with the current state of mouse buttons andwf MSE_BTN,F ; so that clicks are always registered bsf DV_FLAGS,DV_MSES;Signal that the mouse needs service clrf U_FLAGS ;Mouse packet done, await new header bra Main GUMouseDeltaX movlb 3 ;Grab the byte that came in over the UART movf RCREG,W ; " movlb 1 ;Add it to the low byte of the 16-bit delta X addwf MSE_DXL,F ; counter iorlw B'01111111' ;Sign-extend the byte and add this to the high btfss WREG,7 ; byte of the 16-bit delta X counter movlw 0 ; " addwfc MSE_DXH,F ; " decf U_FLAGS,F ;Decrement remaining-bytes counter bra Main GUMouseDeltaY movlb 3 ;Grab the byte that came in over the UART movf RCREG,W ; " movlb 1 ;Add it to the low byte of the 16-bit delta Y addwf MSE_DYL,F ; counter iorlw B'01111111' ;Sign-extend the byte and add this to the high btfss WREG,7 ; byte of the 16-bit delta Y counter movlw 0 ; " addwfc MSE_DYH,F ; " decf U_FLAGS,F ;Decrement remaining-bytes counter bra Main GUForwardByte movlb 3 ;Retransmit the byte that came in over the UART movf RCREG,W ; " movwf TXREG ; " decf U_FLAGS,F ;Decrement the counter in the UART flags movf U_FLAGS,W ;If the counter has reached zero, lower all andlw B'00001111' ; flags and await a new header byte btfsc STATUS,Z ; " clrf U_FLAGS ; " bra Main GUNewHeader movlb 3 ;Grab the byte that came in over the UART movf RCREG,W ; " btfss WREG,7 ;All header bytes have MSB set; if it's clear, clrf TXREG ; relay a zero; this way the host can send a btfss WREG,7 ; string of zero bytes to get all units to a bra Main ; known state bsf U_FLAGS,U_HEADR ;Raise the flag that we have a header byte bsf U_FLAGS,U_CNTR0 ;We'll be receiving at least one byte btfsc WREG,6 ;If bit 6 of the header byte is set, this is a bsf U_FLAGS,U_ISMSE ; mouse packet, so raise that flag btfsc WREG,6 ;If receiving a mouse packet, it has 3 bytes so bsf U_FLAGS,U_CNTR1 ; add 2 to the counter in the flags register btfsc WREG,5 ;If any of the incoming byte's address bits are bsf U_FLAGS,U_FWD ; set, then we need to forward it btfsc WREG,4 ; " bsf U_FLAGS,U_FWD ; " btfsc WREG,3 ; " bsf U_FLAGS,U_FWD ; " btfsc WREG,2 ; " bsf U_FLAGS,U_FWD ; " btfsc WREG,1 ; " bsf U_FLAGS,U_FWD ; " btfsc WREG,0 ; " bsf U_FLAGS,U_FWD ; " btfss U_FLAGS,U_FWD ;If we're not forwarding, we're done here bra Main ; " decf WREG,W ;Decrement the address and then pass the header movwf TXREG ; byte forward to the next device bra Main GotReset movlb 1 movlw 0x22 ;Keyboard register 3 puts it at address 0x2, movwf KBD_3_H ; with SRQ enabled and handler ID 2 movlw 0x02 movwf KBD_3_L movlw 0x23 ;Mouse register 3 puts it at address 0x3, with movwf MSE_3_H ; SRQ enabled and handler ID 1 movlw 0x01 movwf MSE_3_L bcf RX_FLAGS,RX_RST bra Main GotCmd bcf RX_FLAGS,RX_CMD ;Clear the command flag movf ADB_CMD,W ;If the low four bits of the command are zeroes andlw B'00001111' ; then this is a SendReset command and should btfsc STATUS,Z ; be treated the same as a long reset pulse bra GotReset ; " andlw B'00001100' ;If bits 3-2 of the command are 0b11, this is xorlw B'00001100' ; a talk command; otherwise, it's a flush which btfss STATUS,Z ; we ignore or a listen which we'll handle when bra Main ; we get the data that comes along with it movlb 1 ;Compare the address of the talk command with swapf ADB_CMD,W ; the stored address for the keyboard; if they xorwf KBD_3_H,W ; match, this is a talk command for the andlw B'00001111' ; keyboard btfsc STATUS,Z ; " bra KeyboardTalk ; " swapf ADB_CMD,W ;Compare the address of the talk command with xorwf MSE_3_H,W ; the stored address for the mouse; if they andlw B'00001111' ; match, this is a talk command for the mouse btfsc STATUS,Z ; " bra MouseTalk ; " movf FSR0L,W ;If the keyboard queue is not empty or the xorwf FSR1L,W ; mouse service flag is up, we need our devices btfsc STATUS,Z ; to be polled, so send a service request btfsc DV_FLAGS,DV_MSES; " call ServiceRequest ; " bra Main KeyboardTalk movf ADB_CMD,W ;If this is a talk register 0, handle that andlw B'00000011' ; " btfsc STATUS,Z ; " bra KeyboardTalk0 ; " addlw -1 ;If this is a talk register 1, we have no btfsc STATUS,Z ; handler for that, so done bra Main ; " addlw -1 ;If this is a talk register 2, handle that btfsc STATUS,Z ; " bra KeyboardTalk2 ; " bra KeyboardTalk3 ;Else it's a talk register 3, handle that KeyboardTalk0 btfsc DV_FLAGS,DV_MSES;If the mouse needs service, effect a service call ServiceRequest ; request condition so the computer polls it movf FSR0L,W ;If the keyboard queue is empty, we have no xorwf FSR1L,W ; data, so bail btfsc STATUS,Z ; " bra Main ; " moviw FSR1++ ;Pop the next keyboard code off the queue bcf FSR1L,7 ; " movwf ADB_BUF ;Copy it into the ADB buffer clrf ADB_BUF2 ;Put the customary 0xFF into the second byte of decf ADB_BUF2,F ; the ADB buffer xorlw 0x7F ;If the keyboard code was 0x7F, the reset key, btfsc STATUS,Z ; we need to put it in both bytes of what we bcf ADB_BUF2,7 ; send to the computer xorlw 0x7F ;Update keyboard register 2 according to this call UpdateKeyboard2 ; key press or release bsf TX_FLAGS,TX_RDY ;Set up the transmitter flags so we signal that bcf TX_FLAGS,TX_SIZ2; we're ready to transmit two bytes bcf TX_FLAGS,TX_SIZ1; " bsf TX_FLAGS,TX_SIZ0; " bra Main KeyboardTalk2 movlb 1 ;Move the contents of keyboard register 2 into movf KBD_2_H,W ; the ADB buffer and signal the transmitter movwf ADB_BUF ; that we're ready to transmit two bytes bsf TX_FLAGS,TX_RDY ; " bcf TX_FLAGS,TX_SIZ2; " bcf TX_FLAGS,TX_SIZ1; " bsf TX_FLAGS,TX_SIZ0; " movf KBD_2_L,W ; " movwf ADB_BUF2 ; " bra Main KeyboardTalk3 movlb 1 ;Move the high byte of keyboard register 3 into movf KBD_3_H,W ; the ADB buffer with its low nibble masked off andlw B'11110000' ; " movwf ADB_BUF ; " movlb 0 ;Get a pseudorandom four-bit number and put it movf TMR1H,W ; into the low nibble of the ADB buffer; this xorwf TMR1L,W ; way we replace address (which the host andlw B'00001111' ; already knows) with a random number, which iorwf ADB_BUF,F ; helps with collision detection bsf TX_FLAGS,TX_RDY ;Signal the transmitter that we're ready to bcf TX_FLAGS,TX_SIZ2; transmit two bytes bcf TX_FLAGS,TX_SIZ1; " bsf TX_FLAGS,TX_SIZ0; " movlb 1 ;Move the low byte of keyboard register 3 into movf KBD_3_L,W ; the ADB buffer movwf ADB_BUF2 ; " bra Main MouseTalk movf ADB_CMD,W ;If this is a talk register 0, handle that andlw B'00000011' ; " btfsc STATUS,Z ; " bra MouseTalk0 ; " addlw -1 ;If this is a talk register 1, we have no btfsc STATUS,Z ; handler for that, so done bra Main ; " addlw -1 ;If this is a talk register 2, we have no btfsc STATUS,Z ; handler for that, so done bra Main ; " bra MouseTalk3 ;Else it's a talk register 3, handle that Main movlb 0 btfsc PIR1,RCIF bra GotUartByte btfsc RX_FLAGS,RX_RST bra GotReset btfsc RX_FLAGS,RX_CMD bra GotCmd btfsc RX_FLAGS,RX_DATA bra GotData btfsc RX_FLAGS,RX_ABRT bcf RX_FLAGS,RX_ABRT btfsc TX_FLAGS,TX_COL bra GotCollision bra Main MouseTalk0 movf FSR0L,W ;If the keyboard queue is not empty, we have xorwf FSR1L,W ; data for the keyboard, so effect a service btfss STATUS,Z ; request so the computer knows to poll the call ServiceRequest ; keyboard btfss DV_FLAGS,DV_MSES;If the mouse doesn't need service, we're done bra Main ; " bcf DV_FLAGS,DV_MSES;Clear the mouse-needs-service flag MT0Y movlb 1 ;We handle the Y delta differently depending on btfsc MSE_DYH,7 ; whether it's negative or positive bra MT0YNeg ; " MT0YPos movlw 0xC1 ;If it's positive, subtract 63 from it addwf MSE_DYL,F ; " movlw 0xFF ; " addwfc MSE_DYH,F ; " btfsc STATUS,C ;If it didn't borrow, leave the difference, bra MT0YPMo ; it's more than we can convey in one delta movlw B'00111111' ;If it borrowed, add 63 to the difference and addwf MSE_DYL,W ; that's our final delta movwf ADB_BUF ; " clrf MSE_DYH ; " clrf MSE_DYL ; " bra MT0X ;Go to handle the X delta MT0YPMo movlw B'00111111' ;If it didn't borrow, send the delta 63 and movwf ADB_BUF ; signal that we need to be serviced again to bsf DV_FLAGS,DV_MSES; convey the rest of it bra MT0X ;Go to handle the X delta MT0YNeg movlw 0x40 ;If it's negative, add 64 to it addwf MSE_DYL,F ; " movlw 0 ; " addwfc MSE_DYH,F ; " btfss STATUS,C ;If it didn't overflow, leave the sum, it's bra MT0YNMo ; more than we can convey in one delta movlw B'01000000' ;If it overflowed, add -64 to the sum and addwf MSE_DYL,W ; that's our final delta movwf ADB_BUF ; " clrf MSE_DYL ; " bra MT0X ;Go to handle the X delta MT0YNMo movlw B'01000000' ;If it didn't overflow, send the delta -64 and movwf ADB_BUF ; signal that we need to be serviced again to bsf DV_FLAGS,DV_MSES; convey the rest of it MT0X btfsc MSE_DXH,7 ;We handle the X delta differently depending on bra MT0XNeg ; whether it's negative or positive MT0XPos movlw 0xC1 ;If it's positive, subtract 63 from it addwf MSE_DXL,F ; " movlw 0xFF ; " addwfc MSE_DXH,F ; " btfsc STATUS,C ;If it didn't borrow, leave the difference, bra MT0XPMo ; it's more than we can convey in one delta movlw B'00111111' ;If it borrowed, add 63 to the difference and addwf MSE_DXL,W ; that's our final delta movwf ADB_BUF2 ; " clrf MSE_DXH ; " clrf MSE_DXL ; " bra MT0Btns ;Go to handle the X delta MT0XPMo movlw B'00111111' ;If it didn't borrow, send the delta 63 and movwf ADB_BUF2 ; signal that we need to be serviced again to bsf DV_FLAGS,DV_MSES; convey the rest of it bra MT0Btns ;Go to handle the X delta MT0XNeg movlw 0x40 ;If it's negative, add 64 to it addwf MSE_DXL,F ; " movlw 0 ; " addwfc MSE_DXH,F ; " btfss STATUS,C ;If it didn't overflow, leave the sum, it's bra MT0XNMo ; more than we can convey in one delta movlw B'01000000' ;If it overflowed, add -64 to the sum and addwf MSE_DXL,W ; that's our final delta movwf ADB_BUF2 ; " clrf MSE_DXL ; " bra MT0Btns ;Go to handle the X delta MT0XNMo movlw B'01000000' ;If it didn't overflow, send the delta -64 and movwf ADB_BUF2 ; signal that we need to be serviced again to bsf DV_FLAGS,DV_MSES; convey the rest of it MT0Btns btfsc MSE_BTN,0 ;Copy the mouse button bits bsf ADB_BUF,7 ; " btfsc MSE_BTN,1 ; " bsf ADB_BUF2,7 ; " bsf TX_FLAGS,TX_RDY ;Signal the transmitter that we're ready to bcf TX_FLAGS,TX_SIZ2; transmit two bytes bcf TX_FLAGS,TX_SIZ1; " bsf TX_FLAGS,TX_SIZ0; " movlw B'11111111' ;Overwrite the mouse button bits with ones so movwf MSE_BTN ; the mouse buttons can be released bra Main MouseTalk3 movlb 1 ;Move the high byte of mouse register 3 into movf MSE_3_H,W ; the ADB buffer with its low nibble masked off andlw B'11110000' ; " movwf ADB_BUF ; " movlb 0 ;Get a pseudorandom four-bit number and put it movf TMR1H,W ; into the low nibble of the ADB buffer; this xorwf TMR1L,W ; way we replace address (which the host andlw B'00001111' ; already knows) with a random number, which iorwf ADB_BUF,F ; helps with collision detection bsf TX_FLAGS,TX_RDY ;Signal the transmitter that we're ready to bcf TX_FLAGS,TX_SIZ2; transmit two bytes bcf TX_FLAGS,TX_SIZ1; " bsf TX_FLAGS,TX_SIZ0; " movlb 1 ;Move the low byte of mouse register 3 into the movf MSE_3_L,W ; ADB buffer movwf ADB_BUF2 ; " bra Main GotData bcf RX_FLAGS,RX_DATA;Clear the data flag movf ADB_CMD,W ;If bits 3-2 of the command are 0b10, this is andlw B'00001100' ; a listen command and we've now got the data xorlw B'00001000' ; for it; it should be if we're here, but check btfss STATUS,Z ; just in case bra Main ; " movlb 1 ;Compare the address of the listen command with swapf ADB_CMD,W ; the stored address for the keyboard; if they xorwf KBD_3_H,W ; match, this is a talk command for the andlw B'00001111' ; keyboard btfsc STATUS,Z ; " bra KeyboardListen ; " swapf ADB_CMD,W ;Compare the address of the listen command with xorwf MSE_3_H,W ; the stored address for the mouse; if they andlw B'00001111' ; match, this is a talk command for the mouse btfsc STATUS,Z ; " bra MouseListen ; " bra Main ;Else, this is not for one of our devices KeyboardListen movf ADB_CMD,W ;We only care about listen commands for andlw B'00000011' ; register 3, so if this isn't one of those, xorlw B'00000011' ; we're done btfss STATUS,Z ; " bra Main ; " movf ADB_BUF2,W ;If the low byte of the listen register 3 data btfsc STATUS,Z ; is 0x00, handle this bra KeyboardL3_00 ; " addlw 2 ;If the low byte of the listen register 3 data btfsc STATUS,Z ; is 0xFE, handle this bra KeyboardL3_FE ; " addlw 251 ;If the low byte of the listen register 3 data btfsc STATUS,Z ; is 0x02 or 0x03, these are handler IDs that bra KeyboardL3ChgH ; we (as an extended keyboard) understand, so addlw 1 ; change our handler ID accordingly btfsc STATUS,Z ; " bra KeyboardL3ChgH ; " bra Main KeyboardL3_00 movf ADB_BUF,W ;Change the address and flags in register 3 movlb 1 ; according to the first (high) data byte movwf KBD_3_H ; " bra Main KeyboardL3_FE btfsc DV_FLAGS,DV_KBDC;If there was a collision, do not change the bra KL3FENo ; address movlw B'00001111' ;Snuff the top nibble of the first data byte, andwf ADB_BUF,F ; we don't care about those bits movlb 1 ;Copy the address from the first (high) data movf KBD_3_H,W ; byte into keyboard register 3 andlw B'11110000' ; " iorwf ADB_BUF,W ; " movwf KBD_3_H ; " KL3FENo bcf DV_FLAGS,DV_KBDC;Clear the collision flag if it was set bra Main KeyboardL3ChgH movf ADB_BUF2,W ;Change our handler ID (the low byte of movlb 1 ; register 3) to the byte given in the listen movwf KBD_3_L ; register 3 command bra Main MouseListen movf ADB_CMD,W ;We only care about listen commands for andlw B'00000011' ; register 3, so if this isn't one of those, xorlw B'00000011' ; we're done btfss STATUS,Z ; " bra Main ; " movf ADB_BUF2,W ;If the low byte of the listen register 3 data btfsc STATUS,Z ; is 0x00, handle this bra MouseL3_00 ; " addlw 2 ;If the low byte of the listen register 3 data btfsc STATUS,Z ; is 0xFE, handle this bra MouseL3_FE ; " bra Main MouseL3_00 movf ADB_BUF,W ;Change the address and flags in register 3 movlb 1 ; according to the first (high) data byte movwf MSE_3_H ; " bra Main MouseL3_FE btfsc DV_FLAGS,DV_MSEC;If there was a collision, do not change the bra ML3FENo ; address movlw B'00001111' ;Snuff the top nibble of the first data byte, andwf ADB_BUF,F ; we don't care about those bits movlb 1 ;Copy the address from the first (high) data movf MSE_3_H,W ; byte into keyboard register 3 andlw B'11110000' ; " iorwf ADB_BUF,W ; " movwf MSE_3_H ; " ML3FENo bcf DV_FLAGS,DV_MSEC;Clear the collision flag if it was set bra Main GotCollision bcf TX_FLAGS,TX_COL ;Clear the collision flag movlb 1 ;Compare the address of the last command with swapf ADB_CMD,W ; the stored address for the keyboard; if they xorwf KBD_3_H,W ; match, this is a collision on the keyboard's andlw B'00001111' ; address btfsc STATUS,Z ; " bsf DV_FLAGS,DV_KBDC; " swapf ADB_CMD,W ;Compare the address of the last command with xorwf MSE_3_H,W ; the stored address for the mouse; if they andlw B'00001111' ; match, this is a collision on the mouse's btfsc STATUS,Z ; address bsf DV_FLAGS,DV_MSEC; " goto Main ;;; Subprograms ;;; ServiceRequest movlb 0 ;Set Timer0 to overflow after 300 us, the movlw -75 ; nominal service request time movwf TMR0 ; " bsf RX_FLAGS,SRQ_ON ;Signal that we're making a service request bcf INTCON,TMR0IF ;Enable the Timer0 interrupt so we end the bsf INTCON,TMR0IE ; service request after the timer overflows movlb 1 ;Pull the ADB line low movlw B'11011111' ; " andwf TRISA,F ; " return UpdateKeyboard2 btfsc WREG,7 ;If MSB is set, a key has been released, else bra Update2KeyUp ; one has been pressed ;fall through Update2KeyDown addlw -51 ;0x33 Delete/Backspace btfsc STATUS,Z ; if keycode matches, bcf KBD_2_H,K2H_DEL ; mark key as down addlw -3 ;0x36 LeftControl btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_LCT; mark key as down addlw -1 ;0x37 Command btfsc STATUS,Z ; if keycode matches, bcf KBD_2_H,K2H_CMD ; mark key as down addlw -1 ;0x38 LeftShift btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_LSH; mark key as down addlw -1 ;0x39 CapsLock btfsc STATUS,Z ; if keycode matches, bcf KBD_2_H,K2H_CAP ; mark key as down addlw -1 ;0x3A LeftOption btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_LOP; mark key as down addlw -13 ;0x47 Clear/NumLock btfsc STATUS,Z ; if keycode matches, bcf KBD_2_L,K2L_CLR ; mark key as down addlw -36 ;0x6B F14/ScrollLock btfsc STATUS,Z ; if keycode matches, bcf KBD_2_L,K2L_SLK ; mark key as down addlw -16 ;0x7B RightShift btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_RSH; mark key as down addlw -1 ;0x7C RightOption btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_ROP; mark key as down addlw -1 ;0x7D RightControl btfsc STATUS,Z ; if keycode matches, bcf KBD_MODS,KMD_RCT; mark key as down addlw -2 ;0x7F Reset btfsc STATUS,Z ; if keycode matches, bcf KBD_2_H,K2H_RST ; mark key as down bra Update2Mods Update2KeyUp andlw B'01111111' ;Clear MSB so we can check released key's code addlw -51 ;0x33 Delete/Backspace btfsc STATUS,Z ; if keycode matches, bsf KBD_2_H,K2H_DEL ; mark key as up addlw -3 ;0x36 LeftControl btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_LCT; mark key as up addlw -1 ;0x37 Command btfsc STATUS,Z ; if keycode matches, bsf KBD_2_H,K2H_CMD ; mark key as up addlw -1 ;0x38 LeftShift btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_LSH; mark key as up addlw -1 ;0x39 CapsLock btfsc STATUS,Z ; if keycode matches, bsf KBD_2_H,K2H_CAP ; mark key as up addlw -1 ;0x3A LeftOption btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_LOP; mark key as up addlw -13 ;0x47 Clear/NumLock btfsc STATUS,Z ; if keycode matches, bsf KBD_2_L,K2L_CLR ; mark key as up addlw -36 ;0x6B F14/ScrollLock btfsc STATUS,Z ; if keycode matches, bsf KBD_2_L,K2L_SLK ; mark key as up addlw -16 ;0x7B RightShift btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_RSH; mark key as up addlw -1 ;0x7C RightOption btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_ROP; mark key as up addlw -1 ;0x7D RightControl btfsc STATUS,Z ; if keycode matches, bsf KBD_MODS,KMD_RCT; mark key as up addlw -2 ;0x7F Reset btfsc STATUS,Z ; if keycode matches, bsf KBD_2_H,K2H_RST ; mark key as up ;fall through Update2Mods bsf KBD_2_H,K2H_CTL ;If either control key is down, reflect that in btfsc KBD_MODS,KMD_LCT; the appropriate bits in keyboard register 2 btfss KBD_MODS,KMD_RCT; " bcf KBD_2_H,K2H_CTL ; " bsf KBD_2_H,K2H_SHF ;If either shift key is down, reflect that in btfsc KBD_MODS,KMD_LSH; the appropriate bits in keyboard register 2 btfss KBD_MODS,KMD_RSH; " bcf KBD_2_H,K2H_SHF ; " bsf KBD_2_H,K2H_OPT ;If either option key is down, reflect that in btfsc KBD_MODS,KMD_LOP; the appropriate bits in keyboard register 2 btfss KBD_MODS,KMD_ROP; " bcf KBD_2_H,K2H_OPT ; " return ;;; State Machines ;;; TxFsa org 0x900 TStartU movlw PULSE_DNS movwf TMR0 retlw low TStartD TStartD movlw PULSE_UPL movwf TMR0 retlw low TData7D TData7D movlw PULSE_DNL btfsc ADB_BUF,7 movlw PULSE_DNS movwf TMR0 retlw low TData7U TData7U movlw PULSE_UPS btfsc ADB_BUF,7 movlw PULSE_UPL movwf TMR0 retlw low TData6D TData6D movlw PULSE_DNL btfsc ADB_BUF,6 movlw PULSE_DNS movwf TMR0 retlw low TData6U TData6U movlw PULSE_UPS btfsc ADB_BUF,6 movlw PULSE_UPL movwf TMR0 retlw low TData5D TData5D movlw PULSE_DNL btfsc ADB_BUF,5 movlw PULSE_DNS movwf TMR0 retlw low TData5U TData5U movlw PULSE_UPS btfsc ADB_BUF,5 movlw PULSE_UPL movwf TMR0 retlw low TData4D TData4D movlw PULSE_DNL btfsc ADB_BUF,4 movlw PULSE_DNS movwf TMR0 retlw low TData4U TData4U movlw PULSE_UPS btfsc ADB_BUF,4 movlw PULSE_UPL movwf TMR0 retlw low TData3D TData3D movlw PULSE_DNL btfsc ADB_BUF,3 movlw PULSE_DNS movwf TMR0 retlw low TData3U TData3U movlw PULSE_UPS btfsc ADB_BUF,3 movlw PULSE_UPL movwf TMR0 retlw low TData2D TData2D movlw PULSE_DNL btfsc ADB_BUF,2 movlw PULSE_DNS movwf TMR0 retlw low TData2U TData2U movlw PULSE_UPS btfsc ADB_BUF,2 movlw PULSE_UPL movwf TMR0 retlw low TData1D TData1D movlw PULSE_DNL btfsc ADB_BUF,1 movlw PULSE_DNS movwf TMR0 retlw low TData1U TData1U movlw PULSE_UPS btfsc ADB_BUF,1 movlw PULSE_UPL movwf TMR0 retlw low TData0D TData0D movlw PULSE_DNL btfsc ADB_BUF,0 movlw PULSE_DNS movwf TMR0 retlw low TData0U TData0U movlw PULSE_UPS btfsc ADB_BUF,0 movlw PULSE_UPL movwf TMR0 movf ADB_BUF2,W ;Rotate the next byte into position, if there movwf ADB_BUF ; is one movf ADB_BUF3,W ; " movwf ADB_BUF2 ; " movf ADB_BUF4,W ; " movwf ADB_BUF3 ; " movf ADB_BUF5,W ; " movwf ADB_BUF4 ; " movf ADB_BUF6,W ; " movwf ADB_BUF5 ; " movf ADB_BUF7,W ; " movwf ADB_BUF6 ; " movf ADB_BUF8,W ; " movwf ADB_BUF7 ; " movf TX_FLAGS,W ;If the counter in the three least significant andlw B'00000111' ; bits of TX_FLAGS has hit zero, next move is btfsc STATUS,Z ; to send a stop bit retlw low TStopD ; " decf TX_FLAGS,F ;Otherwise, decrement the counter and send retlw low TData7D ; another byte TStopD movlw PULSE_DNL movwf TMR0 retlw low TStopU TStopU bcf TX_FLAGS,TX_ON ;Reset everything into receiver mode movlb 7 ; " movlw B'00100000' ; " movwf IOCAN ; " clrf IOCAP ; " clrf IOCAF ; " clrf RX_TDOWN ; " movlw B'00001000' ; " movwf INTCON ; " retlw low TStartU RxFsa0 org 0xA00 R0idle retlw low R0idle nop R0cmd7 bcf ADB_CMD,7 retlw low R0cmd6 R0cmd6 bcf ADB_CMD,6 retlw low R0cmd5 R0cmd5 bcf ADB_CMD,5 retlw low R0cmd4 R0cmd4 bcf ADB_CMD,4 retlw low R0cmd3 R0cmd3 bcf ADB_CMD,3 retlw low R0cmd2 R0cmd2 bcf ADB_CMD,2 retlw low R0cmd1 R0cmd1 bcf ADB_CMD,1 retlw low R0cmd0 R0cmd0 bcf ADB_CMD,0 bsf RX_FLAGS,RX_CMD retlw low R0cmdP R0cmdP retlw low R0dataS nop R0dataS retlw low R0idle nop nop R0data7 bcf ADB_BUF8,7 retlw low R0data6 nop R0data6 bcf ADB_BUF8,6 retlw low R0data5 nop R0data5 bcf ADB_BUF8,5 retlw low R0data4 nop R0data4 bcf ADB_BUF8,4 retlw low R0data3 nop R0data3 bcf ADB_BUF8,3 retlw low R0data2 nop R0data2 bcf ADB_BUF8,2 retlw low R0data1 nop R0data1 bcf ADB_BUF8,1 retlw low R0data0 nop R0data0 bcf ADB_BUF8,0 movf ADB_BUF8,W R0stxxx btfsc RX_FLAGS,RX_SIZ2 bra R0st1xx R0st0xx btfsc RX_FLAGS,RX_SIZ1 bra R0st01x R0st00x btfsc RX_FLAGS,RX_SIZ0 bra R0st001 R0st000 movwf ADB_BUF retlw low R0dataN R0st001 movwf ADB_BUF2 retlw low R0dataN R0st01x btfsc RX_FLAGS,RX_SIZ0 bra R0st011 R0st010 movwf ADB_BUF3 retlw low R0dataN R0st011 movwf ADB_BUF4 retlw low R0dataN R0st1xx btfsc RX_FLAGS,RX_SIZ1 bra R0st11x R0st10x btfsc RX_FLAGS,RX_SIZ0 bra R0st101 R0st100 movwf ADB_BUF5 retlw low R0dataN R0st101 movwf ADB_BUF6 retlw low R0dataN R0st11x btfsc RX_FLAGS,RX_SIZ0 bra R0st111 R0st110 movwf ADB_BUF7 retlw low R0dataN R0st111 bsf RX_FLAGS,RX_DATA retlw low R0idle R0dataN incf RX_FLAGS,F bcf ADB_BUF8,7 retlw low R0data6 RxFsa1 org 0xB00 R1idle retlw low R1idle nop R1cmd7 bsf ADB_CMD,7 retlw low R1cmd6 R1cmd6 bsf ADB_CMD,6 retlw low R1cmd5 R1cmd5 bsf ADB_CMD,5 retlw low R1cmd4 R1cmd4 bsf ADB_CMD,4 retlw low R1cmd3 R1cmd3 bsf ADB_CMD,3 retlw low R1cmd2 R1cmd2 bsf ADB_CMD,2 retlw low R1cmd1 R1cmd1 bsf ADB_CMD,1 retlw low R1cmd0 R1cmd0 bsf ADB_CMD,0 bsf RX_FLAGS,RX_CMD retlw low R1cmdP R1cmdP retlw low R1idle nop R1dataS movlw B'11111000' andwf RX_FLAGS,F retlw low R1data7 R1data7 bsf ADB_BUF8,7 retlw low R1data6 nop R1data6 bsf ADB_BUF8,6 retlw low R1data5 nop R1data5 bsf ADB_BUF8,5 retlw low R1data4 nop R1data4 bsf ADB_BUF8,4 retlw low R1data3 nop R1data3 bsf ADB_BUF8,3 retlw low R1data2 nop R1data2 bsf ADB_BUF8,2 retlw low R1data1 nop R1data1 bsf ADB_BUF8,1 retlw low R1data0 nop R1data0 bsf ADB_BUF8,0 movf ADB_BUF8,W R1stxxx btfsc RX_FLAGS,RX_SIZ2 bra R1st1xx R1st0xx btfsc RX_FLAGS,RX_SIZ1 bra R1st01x R1st00x btfsc RX_FLAGS,RX_SIZ0 bra R1st001 R1st000 movwf ADB_BUF retlw low R1dataN R1st001 movwf ADB_BUF2 retlw low R1dataN R1st01x btfsc RX_FLAGS,RX_SIZ0 bra R1st011 R1st010 movwf ADB_BUF3 retlw low R1dataN R1st011 movwf ADB_BUF4 retlw low R1dataN R1st1xx btfsc RX_FLAGS,RX_SIZ1 bra R1st11x R1st10x btfsc RX_FLAGS,RX_SIZ0 bra R1st101 R1st100 movwf ADB_BUF5 retlw low R1dataN R1st101 movwf ADB_BUF6 retlw low R1dataN R1st11x btfsc RX_FLAGS,RX_SIZ0 bra R1st111 R1st110 movwf ADB_BUF7 retlw low R1dataN R1st111 bsf RX_FLAGS,RX_DATA retlw low R1idle R1dataN incf RX_FLAGS,F bsf ADB_BUF8,7 retlw low R1data6 RxFsaA org 0xC00 RAidle retlw low RAcmd7 nop RAcmd7 retlw low RAcmd7 nop RAcmd6 retlw low RAcmd7 nop RAcmd5 retlw low RAcmd7 nop RAcmd4 retlw low RAcmd7 nop RAcmd3 retlw low RAcmd7 nop RAcmd2 retlw low RAcmd7 nop RAcmd1 retlw low RAcmd7 nop RAcmd0 retlw low RAcmd7 nop nop RAcmdP retlw low RAcmd7 nop RAdataS retlw low RAcmd7 nop nop RAdata7 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata6 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata5 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata4 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata3 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata2 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata1 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RAdata0 bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop RAdataN bsf RX_FLAGS,RX_ABRT retlw low RAcmd7 nop RxFsaS org 0xD00 RSidle retlw low RSidle nop RScmd7 retlw low RSidle nop RScmd6 retlw low RSidle nop RScmd5 retlw low RSidle nop RScmd4 retlw low RSidle nop RScmd3 retlw low RSidle nop RScmd2 retlw low RSidle nop RScmd1 retlw low RSidle nop RScmd0 retlw low RSidle nop nop RScmdP retlw low RSdataS nop RSdataS retlw low RSidle nop nop RSdata7 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata6 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata5 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata4 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata3 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata2 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata1 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RSdata0 bsf RX_FLAGS,RX_ABRT retlw low RSidle nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop RSdataN bsf RX_FLAGS,RX_ABRT retlw low RSidle nop RxFsaR org 0xE00 RRidle bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd7 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd6 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd5 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd4 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd3 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd2 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd1 bsf RX_FLAGS,RX_RST retlw low RRidle RRcmd0 bsf RX_FLAGS,RX_RST retlw low RRidle nop RRcmdP bsf RX_FLAGS,RX_RST retlw low RRidle RRdataS bsf RX_FLAGS,RX_RST retlw low RRidle nop RRdata7 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata6 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata5 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata4 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata3 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata2 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata1 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RRdata0 bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop RRdataN bsf RX_FLAGS,RX_ABRT bsf RX_FLAGS,RX_RST retlw low RRidle RxFsaT org 0xF00 RTidle retlw low RTidle nop RTcmd7 retlw low RTidle nop RTcmd6 retlw low RTidle nop RTcmd5 retlw low RTidle nop RTcmd4 retlw low RTidle nop RTcmd3 retlw low RTidle nop RTcmd2 retlw low RTidle nop RTcmd1 retlw low RTidle nop RTcmd0 retlw low RTidle nop nop RTcmdP retlw low RTcmdP ;A timeout during 'Tlt' is not a timeout nop RTdataS retlw low RTidle nop nop RTdata7 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata6 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata5 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata4 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata3 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata2 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata1 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop RTdata0 bsf RX_FLAGS,RX_ABRT retlw low RTidle nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop nop RTdataN bsf RX_FLAGS,RX_DATA retlw low RTidle nop ;;; End of Program ;;; end