        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 <P16C84.INC>

        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<<POTX) | (1<<POTY))
        movwf   TRISB
        goto    p4e

        goto    $+1
Beir2   movf    INDF,W          ;x-y=0, or the second TRISB write
        movwf   TRISB

p4e
p41     btfss   INTCON,T0IF     ;Poll timer (finishes at the end of
        goto    p41             ;the 128 cycles long period)

        bcf     STATUS,RP0
                                ;Some additional administration - and delay
        bcf     INTCON,T0IE     ;Disable timer interrupt
        bcf     INTCON,T0IF     ;Clear TMR interrupt flag
        bsf     INTCON,INTE     ;...and enable the external one
        bcf     INTCON,INTF
        movlw   LOW Pirq1       ;set next IRQ
        movwf   irql
        bcf     wflags,TRACE2   ;enable polled rs232 cnt update
        movlw   2
        addwf   sch,F           ;Add time
        clrf    prev            ;...and any additional delay will be taken
                                ;into account correctly, too (since TMR0
                                ;was set properly and still counts the cycles)
        movf    ptemp,W
        movwf   FSR             ;restore FSR

        movf    outbuf,W
        bsf     STATUS,RP0      ;Release POTX/Y outputs
        movwf   TRISB
        bcf     STATUS,RP0
        goto    irqe            ;finish


;--- irq handler when joystick mode is selected ---

Joyirq  movlw   256-(64-13)     ;Set timer
                                ;64
                                ;-3 (irq accept + jump)
                                ;-2 (tmr0 stops after write)
                                ;-6 (pha, php, setirqadd, jump)
                                ;-2 (set_tim)
        movwf   TMR0            ;reload timer
        bcf     INTCON,T0IF     ;Clear timer irq flag
        clrwdt                  ;Clear watchdog timer
        incf    sch,F           ;Increase rs232 timer

                                ;joystick position handle part.
        decfsz  stickcnt,W      ;Decrease joystick prescaler counter
        goto    _i2             ;x part is only checked if stickcnt=0

        bsf     outbuf,RIGHT
        bsf     outbuf,LEFT     ;initialize joystick direction bits to off

        movf    x,W             ;load current x position
        btfsc   STATUS,Z        ;if 0, see ya
        goto    _noxmov
        btfsc   x,7             ;negative?
        goto    _left           ;not, jump to the _left
        bcf     outbuf,RIGHT    ;set RIGHT line bit
        decf    x,F             ;decrease x position value
        goto    _noxmov         ;see ya

_left   bcf     outbuf,LEFT     ;set LEFT line bit,
        incf    x,F             ;increase x value

_noxmov movlw   60              ;y position is checked only if stickcnt=30
_i2     movwf   stickcnt
        sublw   30
        btfss   STATUS,Z
        goto    _noymov

        bsf     outbuf,UP
        bsf     outbuf,DOWN     ;initialize joystick direction bits to off

        movf    y,W             ;same as above, with y position.
        btfsc   STATUS,Z
        goto    _noymov
        btfsc   y,7
        goto    _up
        bcf     outbuf,DOWN
        decf    y,F
        goto    _noymov

_up     bcf     outbuf,UP
        incf    y,F

_noymov

        movf    outbuf,W
        bsf     STATUS,RP0
        movwf   TRISB           ;set the resulting direction bits to TRISB
        bcf     STATUS,RP0

irqe    swapf   sstack,W
        movwf   STATUS          ;Pull STATUS
        swapf   wstack,F
        swapf   wstack,W        ;Pull W
        retfie


;Simple serial receiver routine. The timing is based on the sch variable
;that is incremented in the Irq service routine. Most mice need
;1200 baud. Sch is increased 13 times during one bit time.
;As different mice send data with different protocols, this routine
;supports either 7 or 8 bits data (with one stop bit). No parity check is
;done. Serin assumes 8 data bits if wflags.MOUSE_T is set.
;The serial data is returned in the reg addressed by FSR.

Serin
        clrwdt                  ;Clear that *** watchdog (the prg loops here)
        clrf    scl             ;Clear serial counter as long
        clrf    sch             ;as no startbit is sensed on SERPIN
        movf    TMR0,W
        movwf   prev
#ifdef INVERT
s1      btfss   PORTA,SERPIN
#else
s1      btfsc   PORTA,SERPIN
#endif
        goto    Serin
        btfss   INTCON,T0IE     ;Skip calling serial counter update if
        call    U_sercnt        ;it is taken care by the IRQ service

        movlw   4               ;LDA startcnt
        subwf   sch,W           ;CMP #$04
        btfss   STATUS,C        ;BCS s1  :-)
        goto    s1              ; = wait while >=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<<POTX)
        movwf   mskt            ;POTX output mask
        movf    x,W
        andlw   03fh            ;Strip bit6 and bit7 from X, and store it
        movwf   by2             ;temporarily (--> 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<<POTY)  ;Seems like Y was less than X...
        movwf   mskt            ;so swap them. As the first POT mask, set
        movf    by2,W           ;POTY.
        movwf   temp
        movf    by3,W
        movwf   by2
        movf    temp,w
        movwf   by3
        goto    _cl3

_cleq   movlw   0ffh-((1<<POTX) | (1<<POTY))
        movwf   mskt            ;Equal, so set both masks.

_cl3    clrf    temp            ;Use temp later as offset (0 or -1)
        movf    by2,W
        subwf   by3,F           ;Quickly generate the difference, and store
                                ;(with the preparation below, always positive)
        movf    by2,W           ;Take the 'smaller' value
        btfsc   STATUS,Z
        goto    _clV0           ;it is zero, skip delay line 1 completely
        addlw   0ffh            ;DEC W
        btfsc   STATUS,Z
        goto    _clnoV0         ;x is one, do as above but set offset -1
        addlw   0ffh
        sublw   LOW V0          ;2 or above - subtract it from the address
                                ;of 'V0', a label at the end of the delay
                                ;line. Set first jump's address to the
        movwf   INDF            ;calculated position then store...
        incf    FSR,F           ;INC delay line program pointer...
        goto    _clV0

_clnoV0 decf    temp,F          ;Set -1 offset if x=1
_clV0   movf    by3,W           ;Now take the diff value...
        btfsc   STATUS,Z
        goto    _sb21           ;y-x = 0
        addlw   0ffh
        btfsc   STATUS,Z
        goto    _sb13           ;y-x = 1
        addlw   0ffh
        btfsc   STATUS,Z
        goto    _sb12           ;y-x = 2
        addlw   0ffh
        btfsc   STATUS,Z
        goto    _sb11           ;y-x = 3
        goto    _sb1            ;y-x = 4 or above

_sb21   movlw   LOW Beir2       ;Set the appropriate piece and store its
        goto    _sbx            ;address
_sb13   movlw   LOW Beir13
        goto    _sbx
_sb12   movlw   LOW Beir12
        goto    _sbx
_sb11   movlw   LOW Beir11
_sbx    addwf   temp,W          ;Add offset
        movwf   INDF            ;Set next jump address
        incf    FSR,F
        movf    outbuf,W        ;Output pins buffer
        andwf   mskt,W          ;Mask appropriate POT lines
        movwf   INDF            ;then set to the program
        incf    FSR,F
        goto    _clpend

_sb1    movlw   LOW Beir1
        addwf   temp,W          ;Add offset
        movwf   INDF            ;Set next jump address
        incf    FSR,F
        movf    outbuf,W        ;Output pins buffer
        andwf   mskt,W          ;Mask appropriate POT lines
        movwf   INDF            ;then set to the program
        incf    FSR,F

        movlw   4
        subwf   by3,W           ;y-x-4
        btfsc   STATUS,Z
        goto    _sb2            ;y-x=4
        addlw   0ffh
        btfsc   STATUS,Z
        goto    _sb22           ;y-x=5

        movlw   6               ;y-x=6 or above
        subwf   by3,W
        sublw   LOW V0          ;so set delay line again
        movwf   INDF
        incf    FSR,F
        goto    _sb2

_sb22   movlw   0ffh
        btfsc   STATUS,Z
_sb2    movlw   0
        addlw   LOW Beir2
        movwf   INDF
        incf    FSR,F
        movf    outbuf,W
        andlw   0ffh-((1<<POTX) | (1<<POTY)) ;Mask both POT bits
        movwf   INDF

_clpend movf    by4,W           ;Finished, now we can turn on the
        movwf   line            ;generated program
        return

DetectMouse                     ;check if we have PC or M$ mouse installed

        movlw   0               ;Wait a lot before trying
        movwf   by1             ;by1 used as temporary (counter)
_d1     movf    sch,W
        btfss   STATUS,Z
        goto    _d1
        incfsz  by1,F
        goto    _d1

        movlw   by0
        movwf   FSR
_d2     call    Serin           ;Load a 7 bit data from the serial line
        movlw   07fh
        andwf   INDF,W
        sublw   07fh
        btfsc   STATUS,Z
        goto    _d2             ;Got 07fh, try again

        movlw   0f8h            ;mask most significant 5 bits
        andwf   INDF,W
        btfsc   STATUS,Z        ;if it's 1, suppose we have a Micr0$0ft mouse
        goto    _dmss           ;else jump to MouseSys init
        return
_dmss   bsf     wflags,MOUSE_T  ;set Mouse Systems sign
        return


Start
        clrf    wflags          ;Clear everything
        clrf    x
        clrf    y
        movlw   0ffh            ;All pins inactive
        movwf   outbuf
        movwf   line1+1

        bsf     STATUS,RP0
        movlw   (1<<NOT_RBPU) | (1<<PSA) | (1<<PS2)
        movwf   OPTION_REG      ;set prescaler + timer stuff
        bcf     STATUS,RP0

        btfsc   PORTA,CFGPIN    ;The jumper state is read here
        bsf     wflags,EMUL_T
        btfss   PORTA,CFGPIN
        bcf     wflags,EMUL_T

        movlw   (1<<POTX) | (1<<POTY)
        movwf   PORTB           ;All outputs are L active, except POTX/Y
        movlw   line1           ;Better init delay lines - they contain
        movwf   line            ;computed gotos
        movlw   LOW Beir2
        movwf   line1

        movlw   LOW Defirq      ;Set small IRQ handler
        movwf   irql
        clrf    TMR0            ;set timer
        bsf     INTCON,T0IE
        bsf     INTCON,GIE      ;enable default (minimal) irq handler

        call    DetectMouse

        bcf     INTCON,GIE      ;Disable IRQs
        btfsc   wflags,EMUL_T   ;now set the app. IRQ handler
        goto    _set1351

        movlw   60              ;Joystick mode follows...
        movwf   stickcnt
        bcf     INTCON,T0IF
        movlw   LOW Joyirq
        goto    _j1

_set1351
        bsf     STATUS,RP0
        movlw   0ffh - ((1<<POTX) | (1<<POTY))
        movwf   TRISB           ;Drive POTX and POTY, to detect the computer
        bcf     STATUS,RP0      ;pulling POTX low
        bcf     INTCON,T0IE
        bcf     INTCON,T0IF
        bsf     INTCON,INTE     ;Enable external interrupts
        bcf     INTCON,INTF
        movlw   LOW Pirq1       ;Set POT irq

_j1     movwf   irql
        bsf     INTCON,GIE

        btfsc   wflags,MOUSE_T  ;detected, now jump.
        goto    _mousesys1
        btfsc   wflags,EMUL_T
        goto    _microspIn
        goto    _microsjIn

_mousesys1
        btfsc   wflags,EMUL_T
        goto    _mousesyspIn
        goto    _mousesysjIn

;main loops in joystick mode

_microsj                        ;Main loop in Micro$oft/joystick mode
        movlw   by0
        movwf   FSR

_cj1    call    Serin           ;Synchronize to the first byte of the packet.
        btfss   INDF,6          ;Microsoft mice start with a set 6th bit of
        goto    _cj1            ;the first byte of the data pack.

_microsjIn                      ;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 --> 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
