;********************** ; mouse.ASM ; Revision 1.01 (Last Revised Nov. 28, 1999) ; ;(Copyright 1999, David J. Gacke, All Rights Reserved) ; This source code may be used and modified freely as long as credit is given ; to the original author in the source code as well as the documentation of ; any products derived from it. ; ;*************************** DISCLAIMER - LEGAL STUFF... ********************************************* ; PLEASE READ: USE THIS CODE AT YOUR OWN RISK. The author assumes NO responsibility for the use, or misuse of this ; product/source code. The author also assumes NO responsibility for computer equipment damage that may result ; by the use, misuse, or improper construction of this product. It is released on an AS IS BASIS, all liabilities ; are the END USER'S. ; ;****************************************************************************************************** ; ; DEVELOPER NOTES: This is the code for half of a 2 pic set. This code reads the mouse data ; updates the absolute coordinate set stored in the PIC with the relative ; coordinate data received from the mouse, and passes those coordinates ; to the other 16C505 which generates the timing that the CoCo needs for mouse tracking. ;**************************************************************** LIST p=16c505 #include "c:\progra~1\mplab\p16c505.inc" __config _WDT_OFF & _IntRC_OSC_RB4EN & _MCLRE_OFF & _CP_OFF radix dec ;set default radix to decimal ; ********* DEFINED CONSTANTS ******************************* SENSITIVEBIT0 equ 0x00 ; bit number of sensitivity jumper in portc SENSITIVEBIT1 equ 0x01 ; bit number of sensitivity jumper in portc BUTTON1 equ 0x02 ; bit number of buttons in port c BUTTON2 equ 0x03 ; bit number of buttons in port c PICVALIDOUT equ 0x04 ; bit number in port c PICDATAOUT equ 0x05 ; bit number in port c MOUSEDATA equ 0x00 ; bit number of mouse data line on port b MOUSECLOCK equ 0x01 ; bit number of mouse clock line on port b TRISTATEALL equ 0x03 ; bit number of "tristate all" line on port b PICACKIN equ 0x04 ; bit number of mouse clock line on port b PICRESET equ 0x05 ; bit number of mouse clock line on port b LEFTMOUSEBTN equ 0x05 ; bit number in first byte of mouse data RIGHTMOUSEBTN equ 0x04 ; bit number in first byte of raw mouse data PARITYBIT equ 0x00 ; bit number in PARITY byte of the parity bit ; ********* FILE REGISTER ASSIGNMENTS ********************* MOUSEXPOSLSB equ 0x08 ; current mouse x position MOUSEXPOSMSB equ 0x09 ; MOUSEYPOSLSB equ 0x0A ; current mouse y position MOUSEYPOSMSB equ 0x0B MOUSEBYTE0 equ 0x0E ; first byte of raw mouse data MOUSEBYTE1 equ 0x0F ;second byte of raw mouse data MOUSEBYTE2 equ 0x10 ;third byte of raw mouse data TIMEOUTCTR equ 0x11 ;this is the timeout counter BYTEBUFFER equ 0x12 ; this is the temporary byte buffer. When bits are clocked in from mouse, they ; are reconstructed into bytes here PARITY equ 0x13 ;parity bit store in bit 0 of this file register BITCOUNTER equ 0x14 ; this is the counter for the bits DELAYCOUNTER equ 0x15 ; this is a delay counter. a small delay is necessary in between mouse poll requests DELAYCOUNTER2 equ 0x16 ; this is a delay counter. a small delay is necessary in between mouse poll requests SCRATCHBUFFER equ 0x17 ; this is a temporary scratch buffer for doing calculations and bit manipulations ; ********* END OF ASSIGNMENTS **************************** STARTUP CODE H'000' startup: ; Main power up initialization takes place here movwf OSCCAL ;calibrating the internal oscillator movlw B'10001000' option clrf PORTB clrf PORTC clrf FSR movlw 0 movwf STATUS ; must use movlw 0 on status reg to clear, CANNOT USE CLRF movlw B'00011111' ;trisate all lines except PICRESETOUT tris PORTB movlw B'00000011' ;trisate unused lines tris PORTC clrf MOUSEXPOSLSB ;reset mouse x position clrf MOUSEXPOSMSB ; clrf MOUSEYPOSLSB ;reset mouse y position clrf MOUSEYPOSMSB ; clrf BITCOUNTER clrf MOUSEBYTE0 clrf MOUSEBYTE1 clrf MOUSEBYTE2 bsf PORTC,BUTTON1 ;set button 1 high bsf PORTC,BUTTON2 ;set button 2 high movlw B'00000011' ;tristate unused lines tris PORTC clrf TMR0 ; reset the timer mainloop: ; This is the main loop of the program. ; The basic flow of the main loop is... ; 1. Pause between "poll" commands ; 2. Send "poll" command to the mouse ; 3. Check to verify that we received the ACK (0xFA) byte from the mouse ; 4. If so, clock in the 3 bytes of mouse data, if not, go back to step 1. ; 5. Add acceleration to the mouse pointer depending on jumpers ; 6. Update the absolute mouse position with the new relative position updates ; 7. Transmit those coords to the other PIC in the set. ; 8. Goto step 1 movlw 0 ;erase w register movwf DELAYCOUNTER movlw 10 movwf DELAYCOUNTER2 mainloopdelay: decfsz DELAYCOUNTER,1 ;decrement the delaycounter, and write new value into DELAYCOUNTER. repeat 256 times goto mainloopdelay decfsz DELAYCOUNTER2,1 ;decrement the delay counter goto mainloopdelay call startmousedata ; this initializes necessary registers, and gives the mouse a kick to start transers call clockoutmousedata ; clock out the "poll" command call clockinmousedata ; clock in response data (ack comes first) ; verify we got an ACK(0xFA) back from the mouse movlw 0 movwf STATUS ; must use movlw 0 on status reg to clear, CANNOT USE CLRF movlw 0xFA ; move ACK byte into w register subwf BYTEBUFFER,1 ; xor w and f. verify that f they are equal btfss STATUS,Z ; if not equal, goto the top of the loop and try again goto mainloop call clockinmousedata ; read first byte of mouse data movf BYTEBUFFER,0 ; move byte retrieved into w register movwf MOUSEBYTE0 ; call clockinmousedata ; read second byte of mouse data movf BYTEBUFFER,0 ; move byte retrieved into w register movwf MOUSEBYTE1 ; call clockinmousedata ; read third byte of mouse data movf BYTEBUFFER,0 ; move byte retrieved into w register movwf MOUSEBYTE2 ; goto acceleratemouseptr ; this routine adds velocity to the mouse pointer depending upon jumper settings acceleratereturn: call updatemousestatus ; this routine adds the relative position change returned from the mouse ; and updates the button status btfss PORTB,TRISTATEALL call tristateports call xmitmouseposition ; now that we have the new absolute position, transmit it to the other pic. goto mainloop clockinmousedata: movlw 0x01 movwf PARITY ; seed parity register call waitforclocklinelow; the combination of these two should be the start bit. throw it out movlw 0x08 ;load bitcounter with 8 (bits) movwf BITCOUNTER call waitforclocklinehigh countbits: call waitforclocklinelow movlw 0x01 ; set low bit in w BCF STATUS,C ; make sure carry bit is clear rrf BYTEBUFFER,1 ; shift the bits down one btfsc PORTB,MOUSEDATA ;check data line, skip bit set instruction if clear bsf BYTEBUFFER,7 ; set msb bit in BYTEBUFFER btfsc BYTEBUFFER,7 ; check to see if we just got bit. If so, t0ggle parity bit xorwf PARITY,1 ; call waitforclocklinehigh decfsz BITCOUNTER,1 ;decrement bitcounter goto countbits ; goto paritycheck paritycheck: call waitforclocklinelow btfsc PORTB,MOUSEDATA ; if parity bit on, set bit in w register movlw 0x01 ; xorwf PARITY,0 ; do bit compare test to check for parity btfss STATUS,Z ; result should be zero if its good data goto timeoutroutine ; error, return here call waitforclocklinehigh; personally since I got a good parity bit, call waitforclocklinelow; I'll just wait for stop bit, call waitforclocklinehigh;dont care about the result retlw 0 startmousedata: movlw 0 ;mov 0 into TIMEOUTCTR movwf TIMEOUTCTR ; movwf BYTEBUFFER ;reset byte buffer movwf PARITY ;reset parity byte BSF PARITY,PARITYBIT ; seed parity bit to 1 movlw B'11011110' ;set mouse data line to output tris PORTB bcf PORTB,MOUSEDATA ;drive data line low call waitforclocklinelow;once clock line goes low, the data is latched movlw 0xEB movwf BYTEBUFFER ; move 'read data' command into bytebuffer retlw 0 clockoutmousedata: movlw 0x08 ;load bitcounter with 8 (bits) movwf BITCOUNTER countbitsout: call waitforclocklinehigh movlw 0x01 ; set low bit in w BCF STATUS,C ; make sure carry bit is clear btfsc BYTEBUFFER,0 ; check low bit to see if it is set bsf PORTB,MOUSEDATA ; if it is set, drive mousedata line high btfss BYTEBUFFER,0 ; check low bit to see if it is clear bcf PORTB,MOUSEDATA ; drive mousedata line low btfsc BYTEBUFFER,0 ; check to see if bit was set, if so toggle parity xorwf PARITY,1 ; call waitforclocklinelow rrf BYTEBUFFER,1 ; shift the bits down one decfsz BITCOUNTER,1 ;decrement bitcounter goto countbitsout ; goto parityset ;set parity bit if necessary parityset: movlw 0x00 call waitforclocklinehigh btfsc PARITY,PARITYBIT ; if parity bit on, drive data line high bsf PORTB,MOUSEDATA btfss PARITY,PARITYBIT ; if parity bit off, drive data line low bcf PORTB,MOUSEDATA call waitforclocklinelow call waitforclocklinehigh bsf PORTB,MOUSEDATA ; set stop bit call waitforclocklinelow movlw B'11011111' ;ok, reset mouse to input mode tris PORTB call waitforclocklinehigh; once clock goes high again, start checking for new output from mouse retlw 0 tristateports: movlw B'00111111' ;trisate all lines on both ports tris PORTB tris PORTC holdtristatecondition: btfss PORTB,TRISTATEALL ; if this line is pulled low, hold the tristate condition goto holdtristatecondition ; once line goes high, return teh ports to normal operating state movlw B'11011111' ;ok, reset port b to mouse input mode. tris PORTB movlw B'00000011' ;trisate just the unused lines tris PORTC retlw 0 waitforclocklinehigh: movlw 0 movwf TIMEOUTCTR spinclock3: incfsz TIMEOUTCTR,1 ;increment timeout counter if it goes back to 0 timeout incf PCL,1 ;skip next instruction goto timeoutroutine btfss PORTB,MOUSECLOCK ; check for clock line to go high goto spinclock3 retlw 0 waitforclocklinelow: movlw 0 movwf TIMEOUTCTR spinclock4: incfsz TIMEOUTCTR,1 ;increment timeout counter if it goes back to 0 timeout incf PCL,1 ;skip next instruction goto timeoutroutine btfsc PORTB,MOUSECLOCK ; check for clock line to go low goto spinclock4 retlw 0 waitforclocktoggle: movlw 0 movwf TIMEOUTCTR spinclock: incfsz TIMEOUTCTR,1 ;increment timeout counter if it goes back to 0 timeout incf PCL,1 ;skip next instruction goto timeoutroutine btfss PORTB,MOUSECLOCK ; check for clock line to go high goto spinclock ; now wait for low again movlw 0 movwf TIMEOUTCTR spinclock2: incfsz TIMEOUTCTR,1 ;increment timeout counter if it goes back to 0 timeout incf PCL,1 ;skip next instruction goto timeoutroutine btfsc PORTB,MOUSECLOCK ; check for clock line to go low goto spinclock2 retlw 0 clockdatatopic: movlw 0 movwf BITCOUNTER ;reset bitcounter nextbit: btfsc BYTEBUFFER,0 ; check bit 0 set data line on port b to match goto datalinehigh goto datalinelow updatemousestatus: ;button 1 status update btfsc MOUSEBYTE0,0 ;bit 0 of first data byte is left button bcf PORTC,BUTTON1 ; drive line low (switch closed) if bit is high btfss MOUSEBYTE0,0 ;bit 5 of first data byte is left button bsf PORTC,BUTTON1 ; drive line high (switch open) if bit is low ;button 2 status update btfsc MOUSEBYTE0,1 ;bit 1 of first data byte is right button bcf PORTC,BUTTON2 ; drive line low (switch closed) if bit is high btfss MOUSEBYTE0,1 ;bit 1 of first data byte is right button bsf PORTC,BUTTON2 ; drive line high (switch open) if bit is low ;Mouse X Position Update btfsc MOUSEBYTE0,6 ; this is the x-overflow bit. if set, skip x calculation goto updatemousey movf MOUSEBYTE1,0 ; load the relative X position into the w register btfsc MOUSEBYTE0,4 ; test to see if the relative xposition is positive goto subtractx addwf MOUSEXPOSLSB,1 ; add relative position update to the absolute position btfss STATUS, C ; if no carry occured then go update y pos goto updatemousey incf MOUSEXPOSMSB,1 ; then increment the MSB of the xposition movlw 0x08 ; check to see if we exceeded the max value. if so, set it to the max value. subwf MOUSEXPOSMSB,0 ; subtract. check for zero bit. if so, we're too big btfsc STATUS,C ; if the carry bit is set, we exceeded the max (max is currently 0x07FF) goto maximumxpos goto updatemousey ; no goto the mouse y update section subtractx: comf MOUSEBYTE1,0 ; take complement of whats in register movwf SCRATCHBUFFER bcf STATUS,C ; clear the carry bit rlf SCRATCHBUFFER,0 ; rotate bits left (multiply by 2) iorlw 0x01 ; or on bit 0 subwf MOUSEXPOSLSB,1 ; add relative position update to the absolute position btfsc STATUS, C ; if a borrow occured then continue to check to see if XPosition<0 goto updatemousey movlw 0 ; load w register with 0 subwf MOUSEXPOSMSB,1 ; now subtract 0 from mouse x pos. if answer is zero, then xpos now BELOW 0! btfsc STATUS,Z goto zeroxpos decf MOUSEXPOSMSB,1 ; if MSB not already 0, then decrement the MSB of the xposition ;Mouse Y Position Update updatemousey: btfsc MOUSEBYTE0,7 ; this is the y-overflow bit. if set, skip y calculation retlw 0 movf MOUSEBYTE2,0 ; load the relative Y position into the w register btfss MOUSEBYTE0,5 ; test to see if the relative yposition is positive; if positive goto subtract routine goto subtracty comf MOUSEBYTE2,0 ; take complement of whats in register, this way we can invert the y axis movwf SCRATCHBUFFER bcf STATUS,C ; clear the carry bit rlf SCRATCHBUFFER,0 ; rotate bits left (multiply by 2) iorlw 0x01 ; or on bit 0 addwf MOUSEYPOSLSB,1 ; add relative position update to the absolute position btfss STATUS, C ; if no carry occured then return here retlw 0 incf MOUSEYPOSMSB,1 ; then increment the MSB of the yposition movlw 0x08 ; check to see if we exceeded the max value. if so, set it to the max value. subwf MOUSEYPOSMSB,0 ; subtract. check for zero bit. if so, we're too big btfsc STATUS,C ; if the carry bit is set, we exceeded the max (max is currently 0x07FF) goto maximumypos retlw 0 ; if no wrap, return here subtracty: subwf MOUSEYPOSLSB,1 ; add relative position update to the absolute position btfsc STATUS, C ; if a borrow occured then continue to check to see if YPosition<0 retlw 0 movlw 0 ; load w register with 0 subwf MOUSEYPOSMSB,1 ; now subtract 0 from mouse y pos. if answer is zero, then ypos now BELOW 0! btfsc STATUS,Z goto zeroypos decf MOUSEYPOSMSB,1 ; if MSB not already 0, then decrement the MSB of the yposition retlw 0 xmitmouseposition: call resetpic movf MOUSEXPOSLSB,0 ;load LSB of x position into bytebuffer movwf BYTEBUFFER call clockdatatopic movf MOUSEXPOSMSB,0 ;load MSB of x position into bytebuffer movwf BYTEBUFFER call clockdatatopic movf MOUSEYPOSLSB,0 ;load LSB of y position into bytebuffer movwf BYTEBUFFER call clockdatatopic movf MOUSEYPOSMSB,0 ;load MSB of y position into bytebuffer movwf BYTEBUFFER call clockdatatopic retlw 0 resetpic: bcf PORTC,PICVALIDOUT ; drop valid out line waitforresetacklow: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfsc PORTB,PICACKIN ; wait for ack to go low goto waitforresetacklow bsf PORTB,PICRESET ; set reset line high bsf PORTC,PICVALIDOUT ; set valid line high waitforresetackhigh: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfss PORTB,PICACKIN ; wait for ack to go high goto waitforresetackhigh bcf PORTC,PICVALIDOUT ; drop valid out line bcf PORTB,PICRESET ; drop reset line retlw 0 datalinehigh: bcf PORTC,PICVALIDOUT ; drop valid out line acklinelow1: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfsc PORTB,PICACKIN ; wait for ack to go low goto acklinelow1 bsf PORTC,PICDATAOUT bSf PORTC,PICVALIDOUT ; raise valid out line acklinehigh1: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfss PORTB,PICACKIN ; wait for ack to go high goto acklinehigh1 goto nextbitout datalinelow: bcf PORTC,PICVALIDOUT ; drop valid out line acklinelow2: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfsc PORTB,PICACKIN ; wait for ack to go low goto acklinelow2 bcf PORTC,PICDATAOUT bsf PORTC,PICVALIDOUT ; raise valid out line acklinehigh2: btfss PORTB,TRISTATEALL ; if somebody tries to sneak in a tristate here, go ahead and let em goto tristateports btfss PORTB,PICACKIN ; wait for ack to go high goto acklinehigh2 nextbitout: rrf BYTEBUFFER,1 ; rotate BYTEBUFFER right incf BITCOUNTER,1 movlw 8 subwf BITCOUNTER,0 btfss STATUS,Z goto nextbit retlw 0 acceleratemouseptr: btfsc PORTC,SENSITIVEBIT0 ; check to see if mouse value should be doubled goto skipsensitive0 bcf STATUS,C ; clear carry bit rlf MOUSEBYTE1,1 ; double value. mouse isnt as sensitive as we need bcf STATUS,C ; clear carry bit rlf MOUSEBYTE1,1 ; double value. mouse isnt as sensitive as we need bcf STATUS,C ; clear carry bit rlf MOUSEBYTE2,1 ; double value. mouse isnt as sensitive as we need bcf STATUS,C ; clear carry bit rlf MOUSEBYTE2,1 ; double value. mouse isnt as sensitive as we need skipsensitive0: btfsc PORTC,SENSITIVEBIT1 ; check to see if mouse value should be "accelerated" goto skipsensitive1 bcf STATUS,C ; clear carry bit rlf MOUSEBYTE1,1 ; double value. mouse isnt as sensitive as we need bcf STATUS,C ; clear carry bit rlf MOUSEBYTE2,1 ; double value. mouse isnt as sensitive as we need skipsensitive1: goto acceleratereturn ; return from the acceleration subroutine here. zeroxpos: movlw 0 movwf MOUSEXPOSMSB movwf MOUSEXPOSLSB retlw 0 zeroypos: movlw 0 movwf MOUSEYPOSMSB movwf MOUSEYPOSLSB retlw 0 maximumxpos: movlw 0x07 movwf MOUSEXPOSMSB movlw 0xFF movwf MOUSEXPOSLSB goto updatemousey ; use a goto here becuase we havent completed the y calculations yet. maximumypos: movlw 0x07 movwf MOUSEYPOSMSB movlw 0xFF movwf MOUSEYPOSLSB retlw 0 timeoutroutine: movlw B'11011111' ;depending on where things broke, we may need to reset this tris PORTB goto mainloop END