list p=16c84 ; This should be the code of the PIC16c84 MCU in the ultimate serial to ; Commodore mouse interface. ; This program is copyrighted by the Author. However, I consider it being ; under GPL. Be it whatever way, this code was done by me as ; Levente Hársfalvi, (C) 1998,1999,2000 ; Compile the source with MPASM, the freely available PIC assembler from ; Microchip. ; V1.0 1998.06.08 ; Initial release. Also part of my master's thesis. ; All features work. Microsoft bare mouse, Mouse Systems mouse. ; Auto detection of the connected serial mouse. ; Joystick mode only. ; Right and middle buttons mapped to POTX, POTY. ; 'Proportional' movement; movement --> time delay ; V1.1 1999.07.14 ; Added support for Logitech Mouseman (Microsoft mode, weird 3/4 bytes ; packet for middle button). Genius mice seem also support this mode... ; Removed code for switching off and on the TL497 power supply chip ; (was used for switching off the mouse before starting detect procedure). ; Right button to POTY, mid to POTX bug fixed. ; V1.2 2000.09.23 ; Finally, added support for proportional 1351 mouse emulation!!! ; Modified Serin in order to cope with less accurate timing and disabled ; TMR0 interrupt service (also implemented a semaphore, avoiding POT irq ; handler and Serin to confuse each other). ; 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 2 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, write to the Free Software ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ; -------------------------------------------------------------------------- #include Radix DEC __config _XT_OSC ;Uncomment for inverted polarity RS-232 ;#define INVERT irql equ 20h ;irq service routine pointer (LOW) prev equ 21h ;Previous TMR0 value (serial time calculation) line equ 23h ;Delay line program - 'bank' number line1 equ 24h line2 equ 2ah temp equ 1dh temp2 equ 1eh wstack equ 0ch sstack equ 0dh outbuf equ 19h ;output pin buffer register. ;see 'output pins' definitions. SERPIN equ 0 ;serial input pin is RA0 CFGPIN equ 2 ;output mode selector input (joy vs. 1351) scl equ 16h ;Serial counter low sch equ 17h ;High ; equ 1fh Free! ; equ 22h Free! bitcnt equ 10h ;Used by Serin stickcnt equ 1ch ;Joystick mode counter ptemp equ 18h ;Temporary for the POTX/Y irq handler ptemp2 equ 1ch ;(The variables reserve the same cells, since ;they're never used in both roles at the ;same time) by0 equ 11h ;reserved area for the bytes received from by1 equ 12h ;the mouse. (It turns out to be restricted by2 equ 13h ;in this role - only the first bytes are by3 equ 14h ;used for that task, the others are used by by4 equ 15h ;Clcline) mskt equ 0eh ;Temporary for POTX/Y mask in Clcline x equ 1ah ;x position of the pointer. 2's complement y equ 1bh ;guess what... SYNC equ 0 ;POTX going directly to the input (INT) POTX equ 1 ;output pins (POTX ... UP) POTY equ 2 RIGHT equ 3 LEFT equ 4 DOWN equ 5 FIRE equ 6 UP equ 7 wflags equ 0fh ;bit0 : 0=Microsoft mouse ; 1=Mouse Systems mouse ;bit1 : 0=Joystick emulation mode ; 1=1351 mode ;bit2,3 : TRACE bits (kind of semaphore) MOUSE_T equ 0 ;respective bits of the above EMUL_T equ 1 TRACE1 equ 2 TRACE2 equ 3 ; --- Reset handler --- org 00h ; reset vector. clrf PCLATH clrwdt goto Start org 04h ; --- main IRQ handler and redirector --- Irq movwf wstack ;Push W swapf STATUS,W ;Push STATUS movwf sstack movf irql,W ;Load IRQ vector and jump... movwf PCL ; --- default irq handler; updates serial timer only. --- Defirq movlw 256-(64-13) ;Set timer ;64 ;-3 (irq accept + jump) ;-6 (pha, php, setirqadd, jump) ;-2 (set_tim) ;-2 (TMR0 stops at writing) movwf TMR0 ;Reload... bcf INTCON,T0IF ;Clear timer irq flag clrwdt ;Clear watchdog timer incf sch,F ;Increase rs232 timer goto irqe ;All done, get out of here. ; --- Irq handler for 1351 mode - first part... --- Pirq1 movlw 11 ; ;-3 (irq accept + jump) ;-6 (pha, php, setirqadd, jump) ;-2 (movlw+subwf) subwf TMR0,W ;Get timer value at the IRQ request moment movwf ptemp ;keep it by hand... movlw 256-(128-16) ; ;128 ;-3 (irq accept + jump) ;-2 (tmr0 stops after write) ;-6 (pha, php, setirqadd, jump) ;-3 (calculate timer, store) ;-2 (set_tim) movwf TMR0 ;Load... bsf INTCON,T0IE ;Enable timer interrupts bcf INTCON,T0IF ;Clear irq flag bcf INTCON,INTE ;Disable external interrupts clrwdt bsf wflags,TRACE2 ;Inhibit polled RS232 cnt update btfss wflags,TRACE1 ;Skip if updating is currently served call Ui_sercnt ;else update serial counter movlw LOW Pirq2 movwf irql goto irqe Pirq2 movlw 256-(128-13) ;Set timer ;128 ;-3 (irq accept + jump) ;-2 (tmr0 stops after write) ;-6 (pha, php, setirqadd, jump) ;-2 (set_tim) movwf TMR0 ;Reload... bcf INTCON,T0IF ;Clear timer irq flag movlw 2 addwf sch,F ;Increase rs232 timer movlw LOW Pirq3 ;set next phase, since it's time... movwf irql goto irqe ;finished... Pirq3 movlw 256-(64-13-22) ; ;64 ;-3 (irq accept + jump) ;-2 (tmr0 stops after write) ;-6 (pha, php, setirqadd, jump) ;-2 (set_tim) ;-22 (22 cycles advance, to start Pirq4 sooner) movwf TMR0 ;Load... bcf INTCON,T0IF ;Clear timer irq flag movlw 3 addwf sch,F ;Increase rs232 timer movlw LOW Pirq4 movwf irql goto irqe ;finish Pirq4 movlw 256-128-14 ;Set timer ;128 cycles long process ;+12 (cycles until the duty cycle base) ;+2 (tmr0 stops after write) ;(others were calculated into Pirq3) movwf TMR0 ;Reload... bcf INTCON,T0IF ;Clear timer irq flag movf FSR,W movwf ptemp ;preserve FSR register movf line,W ;Load delay line program address movwf FSR bsf STATUS,RP0 ;We'll write to TRISB, or whatever... movf INDF,W ;First jump incf FSR,F movwf PCL ;Jump into the delay line prg goto $+1 ;Simple delay line goto $+1 ;2 cycle NOPs, with jump at the end. goto $+1 ;2+ delay value, because of the jump (4 clk goto $+1 ;cycles) goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 goto $+1 V0 movf INDF,W ;'0-point' incf FSR,F movwf PCL ;'0' time is fit to the minute TRISB is written goto $+1 ;'x=1' is handled by a -1 offset Beir1 movf INDF,W ;Beir = 'Write into' movwf TRISB ;Load byte, write into TRISB incf FSR,F ;and increase pointer movf INDF,W ;Next byte is a jump incf FSR,F ;inc ptr nop ;+1 cycle movwf PCL ;jump ;--> x-y must be 4+ (delay between 2 ;subsequent writes to TRISB) goto $+1 Beir11 movf INDF,W ;Special case: x-y=3 movwf TRISB goto $+1 goto Beir132 ;hardwired jump goto $+1 Beir12 movf INDF,W ;Special case: x-y=2 movwf TRISB goto Beir132 goto $+1 Beir13 movf INDF,W ;x-y=1 movwf TRISB Beir132 andlw 0ffh - ((1<=4 cycles ;startbit (high). Thus we're sure that ;we're somewhere in the first third of the ;start pulse (one third bit time) movlw 4 subwf sch,F ;sub the above from serial counter clrf INDF ;clear data movlw 8 ;set number of databits btfss wflags,MOUSE_T ;7 if Micro$oft, else 8 movlw 7 movwf bitcnt _no2 btfss INTCON,T0IE ;As above, skip serial counter update if call U_sercnt ;TMR0 irq is on movlw 13 subwf sch,W ;wait until sch exceeds 13 btfss STATUS,C ;thus we waited one bit-time goto _no2 movlw 13 subwf sch,F ;Subtract one bit time rrf PORTA,W ;Serial input pin is RA0 ;-) rrf INDF,F ;rotate to the right. decfsz bitcnt,F ;once more, until collected all bits. goto _no2 #ifdef INVERT bsf STATUS,C ;pre-set Carry, according to polarity #else bcf STATUS,C #endif btfss wflags,MOUSE_T ;skip if 8 databits. rrf INDF,F ;else, shift the thing once ;more. #ifdef INVERT comf INDF,F ;Negate like hell _no3 btfsc PORTA,SERPIN ;wait for stop bit, #else _no3 btfss PORTA,SERPIN ;wait for stop bit, #endif goto _no3 return ;then get outta here. ;Update serial counter. It is called whenever the TMR0 IRQ is disabled ;(in other words, 1351 mode is selected and currently no POTX/Y pulse is ;served). Contains semaphore code, to avoid confusion if the IRQ routine ;is activated during the run. U_sercnt clrwdt movf prev,W ;standing at previous run subwf TMR0,W ;spent cycles calculated bsf wflags,TRACE1 ;inhibit updating Sch in the IRQ service btfsc wflags,TRACE2 ;did an IRQ occur meanwhile? goto us_end ;Yes, throw the result away and quit addwf prev,F ;Update previous standing with the diff movwf temp clrf temp2 rlf temp,F rlf temp2,F rlf temp,W ;Multiply diff by 4. Low byte in W rlf temp2,F addwf scl,F ;Add to Scl + write it back movf temp2,W btfsc STATUS,C ;Inc Sch if there was a carry addlw 1 addwf sch,F ;Add high bits us_end bcf wflags,TRACE1 ;Enable IRQ Scl/Sch updates return ;The same as the above, but to be called from the interrupt routine ;(different variables in order to avoid problems) Ui_sercnt movf prev,W subwf ptemp,W addwf prev,F movwf ptemp clrf ptemp2 rlf ptemp,F rlf ptemp2,F rlf ptemp,W rlf ptemp2,F addwf scl,F movf ptemp2,W btfsc STATUS,C addlw 1 addwf sch,F return ;Calculate limitation. The resulting delta will be limited to +-64. ;Position value in W, delta in INDF, returns with limited delta. ;(Called in joystick mode only). Clclimit movwf temp addwf INDF,W movwf temp2 btfss temp,7 ;If old<0 & d<0 & new>=0 then new:=-128 goto _nneg ;'cos we seem to made it underflow btfss INDF,7 goto _npos ;this is neccessary, else our limiter btfsc temp2,7 ;would fail. goto _npos movlw 080h ;-128 goto _npos _nneg btfsc temp,7 ;the same, if it seems to overflow goto _npos ;thus load 127 instead. btfsc INDF,7 ;(case old>0, d>0, new<0) goto _npos btfss temp2,7 goto _npos movlw 07fh _npos movwf temp2 ;everything is O.K., now limiting to btfsc temp2,7 ;+-64 if above goto _chkneg movlw 64 subwf temp2,W movf temp2,W btfsc STATUS,C movlw 64 goto _cle _chkneg movlw 64 addwf temp2,W movf temp2,W btfss STATUS,C movlw -64 _cle movwf temp2 movf temp,W subwf temp2,W return Clcline ;Calculate delay line program movlw line1 movwf by4 ;by4 is used as temporary movwf FSR movf line,W sublw line1 btfss STATUS,Z ;If it was line1, set line2 (and vice versa) goto _cl2 ;(Double buffering) movlw line2 movwf by4 movwf FSR _cl2 movlw 0ffh-(1< 6 bit values) movf y,W ;The same with Y andlw 03fh movwf by3 subwf by2,W ;Compare... btfsc STATUS,Z goto _cleq ;Equal... btfss STATUS,C goto _cl3 movlw 0ffh-(1< Fire button bsf outbuf,FIRE ;Clear flag in outbuf if button pressed btfsc INDF,5 ;--> thus activate that bit in TRISB bcf outbuf,FIRE ;(done in the IRQ service) btfss INDF,4 ;Right button --> POTX bsf outbuf,POTX btfsc INDF,4 bcf outbuf,POTX incf FSR,F call Serin ;Next byte btfsc INDF,6 goto _microsp ;Never lose sync!!! rrf by0,F rrf by0,F rrf by0,W andlw 0c0h iorwf INDF,F ;And now we got the whole 8-bit dX movement movf x,W call Clclimit addwf x,F call Serin ;Next byte btfsc INDF,6 goto _microsp rrf by0,F rrf by0,F rrf by0,W andlw 0c0h iorwf INDF,F ;8-bit Y movement movf y,W call Clclimit addwf y,F movlw by0 ;Do we have a 4th byte? movwf FSR ;If next byte's bit6 is 0, then we do. ;Logitech Mouseman extension --> middle ;button was pressed/depressed. call Serin btfsc INDF,6 goto _microsjIn ;nope, it's a first byte --> treat as it is btfss INDF,5 ;Middle button --> POTY bsf outbuf,POTY btfsc INDF,5 bcf outbuf,POTY goto _microsj _mousesysj ;This is the main loop in Mouse systems mode movlw by0 ;(joystick mode) movwf FSR _cj2 call Serin ;Some sync. MouseSys packets start with a movlw 078h ;0b10000XXX byte (XXX are the buttons) andwf INDF,W btfss STATUS,Z goto _cj2 btfss INDF,7 goto _cj2 _mousesysjIn btfsc INDF,2 ;Left button --> Fire button bsf outbuf,FIRE ;0 --> mouse button was depressed btfss INDF,2 ;(opposite to M$ mice) bcf outbuf,FIRE btfsc INDF,0 bsf outbuf,POTX ;Right button --> POTX btfss INDF,0 bcf outbuf,POTX btfsc INDF,1 ;Middle button --> POTY bsf outbuf,POTY btfss INDF,1 bcf outbuf,POTY call Serin ;Next byte (dX) movf x,W call Clclimit addwf x,F call Serin ;Next byte (dY) comf INDF,F ;Negate(dy). dy is the opposite to the incf INDF,F ;M$ mouse. movf y,W call Clclimit addwf y,F call Serin ;Next byte (dX2, the movement while movf x,W ;transmitting) call Clclimit addwf x,F call Serin ;Next byte (dY2) comf INDF,F incf INDF,F movf y,W call Clclimit addwf y,F goto _mousesysj ;Loop... _microsp ;Main loop in Micro$oft/proportional mode movlw by0 movwf FSR _cp1 call Serin ;Synchronize to the first byte of the packet. btfss INDF,6 ;Microsoft mice start with a set 6th bit of goto _cp1 ;the first byte of the data pack. _microspIn ;Start here if 4th byte failed btfss INDF,5 ;Left button --> Fire button bsf outbuf,FIRE ;Clear flag in outbuf if button pressed btfsc INDF,5 ;--> thus activate that bit in TRISB bcf outbuf,FIRE ;(done in the IRQ service) btfss INDF,4 ;Right button --> UP bsf outbuf,UP btfsc INDF,4 bcf outbuf,UP incf FSR,F call Serin ;Next byte btfsc INDF,6 goto _microsp ;Never lose sync!!! rrf by0,F rrf by0,F rrf by0,W andlw 0c0h iorwf INDF,W ;And now we got the whole 8-bit dX movement addwf x,F call Serin ;Next byte btfsc INDF,6 goto _microsp rrf by0,F rrf by0,F rrf by0,W andlw 0c0h iorwf INDF,W ;8-bit Y movement xorlw 0ffh ;negate! (opposite direction) addlw 1 addwf y,F call Clcline movlw by0 ;Do we have a 4th byte? movwf FSR ;(Logitech Mouseman middle button) call Serin btfsc INDF,6 goto _microspIn ;nope, it's a first byte --> treat as it is btfss INDF,5 ;Middle button --> DOWN bsf outbuf,DOWN btfsc INDF,5 bcf outbuf,DOWN call Clcline goto _microsp _mousesysp ;This is the main loop in Mouse systems mode movlw by0 ;(proportional) movwf FSR _cp2 call Serin ;Some sync. MouseSys packets start with a movlw 078h ;0b10000XXX byte (XXX are the buttons) andwf INDF,W btfss STATUS,Z goto _cp2 btfss INDF,7 goto _cp2 _mousesyspIn btfsc INDF,2 ;Left button --> Fire button bsf outbuf,FIRE ;0 --> mouse button was depressed btfss INDF,2 ;(opposite to M$ mice) bcf outbuf,FIRE btfsc INDF,0 bsf outbuf,UP ;Right button --> UP btfss INDF,0 bcf outbuf,UP btfsc INDF,1 ;Middle button --> DOWN bsf outbuf,DOWN btfss INDF,1 bcf outbuf,DOWN call Serin ;Next byte (dX) movf INDF,W addwf x,F call Serin ;Next byte (dY) movf INDF,W addwf y,F call Clcline ;Update delay line movlw by0 movwf FSR call Serin ;Next byte (dX2, the movement while movf INDF,W ;transmitting) addwf x,F call Serin ;Next byte (dY2) movf INDF,W addwf y,F call Clcline ;Update delay line goto _mousesysp ;Loop... end