;--------------------------------------
;	PS/2 Mouse Host
;
;	This was writen to support a 3-button, 2-axis PS/2 mouse with scroll wheel. It is based
;	on information found on Adam Chapweske's web site at:
;
;	http://panda.cs.ndsu.nodak.edu/%7Eachapwes/PICmicro/mouse/mouse.html
;	http://panda.cs.ndsu.nodak.edu/%7Eachapwes/PICmicro/PS2/ps2.htm
;
;	The web site is an excellent resouce for dealing with the PS/2 protocol and it is 
;	HIGHLY recomended you visit it.
;
;	This code works, but I still consider it to be incomplete since not all features
;	of the PS/2 protocol have been added and/or fully tested. The parity check for 
;	data received from the mouse is not finished. Some of the code that [I think] is
;	needed to implement this feature is in the subroutine "ReceiveByte". There are 3 lines
;	of code in that subroutine that are commented out, they are what I started for the 
;	parity check. There is also no code to check for error commands received from the
;	mouse, and there is no code to send error commands to the mouse. But, even without these
;	features, everything seems to work as it should.
;
;	THIS HAS ONLY BEEN TESTED WITH A 3-BUTTON/SCROLL WHEEL MICROSOFT INTELLIMOUSE!!
;	But in theory, it should support a standard 2-button mouse with no scroll wheel
;	as is. I have been able to setup up the mouse I tested this code with to operate
;	as a 2-button mouse with no scroll wheel by commenting out the scroll wheel check
;	in the subroutine "Initialize Mouse"
;
;	It should also be noted that I am about as far from being an expert at programing SX chips 
;	as anybody can be. Since this was my very first attempt at writing any code from scratch
;	I am fairly certain that this code can be optimised further. It is free to use by anybody
;	for any reason. Use it as is, or change it, modify it, do whatever you want with it.

List Q = 37
DEVICE SX28l, TURBO, STACKX, OSCHS2
IRC_CAL IRC_FAST
FREQ 4_000_000


RESET MAIN

ClockLine	EQU	RA.0	;this is the clock line
DataLine	EQU	RA.1	;this is the data line

		ORG	$08
TxByte		DS	1
BitCount	DS	1
ParityBit	DS	1
ParityCount	DS	1
TempVar		DS	1

		ORG	$10
RxBytes		EQU	$
RxByte1		DS	1
RxByte2		DS	1
RxByte3		DS	1
RxByte4		DS	1
RxTemp		DS	1

		ORG	$30
Counters	EQU	$
Counter1	DS	1
Counter2	DS	1
Counter3	DS	1
Counter4	DS	1
Counter5	DS	1
;------------------------------------------------
; Send one byte to mouse
;------------------------------------------------

SendByte	CLRB	ClockLine		;set Clock line low 
		MOV	!RA, #%1110		;set ClockLine as OUTPUT
		CALL	Delay100		;wait 100 microseconds
		CLRB	DataLine		;bring the Data line low
		MOV	!RA, #%1100		;set DataLine as OUTPUT
		SETB	ClockLine		;set Clock line high
		MOV	!RA, #%1101		;set ClockLine as INPUT
		MOV	BitCount, #8		;sets number of bits to send
		JB	ClockLine, $		;wait for clock line to go low
		SETB	DataLine		;set DataLine high
:TxLoop		JB	ClockLine, $		;wait for Clock line to go low
		RR	TxByte			;get next bit to send, store in carry flag
		SNC				;if bit being sent is high, ParityCount is increased...
		  INC	ParityCount		;  this keeps track of all the high bits, used for parity check
		SC				;if bit being sent is 0...
		  CLRB	DataLine		;  set Data Line low
		SNC				;if bit being sent is 1...
		  SETB	DataLine		;  set Data Line high
		JNB	ClockLine, $		;wait for Clock Line to go high
		DECSZ	BitCount		;decrease BitCount
		  JMP	:TxLoop			;if BitCount > 0 loop again
		JB	ClockLine, $		;wait for Clock Line to go low
		SB	ParityCount.0		;if bit 0 of ParityCount is 0...
		  SETB	DataLine		;  set Data Line high
		SNB	ParityCount.0		;if bit 0 of ParityCoundt is 1...
		  CLRB	DataLine		;  set DataLine low
		MOV	!RA, #%1111		;set DataLine as INPUT
		JB	ClockLine, $		;wait for clockline to go low
		JB	DataLine, $		;Wait dataline to go low, this is acknowledge bit 
 		JNB	ClockLine, $		;wait for clockline to go high
		JNB	DataLine, $		;wait for dataline to go high
		CLR	TxByte
		CLR	ParityCount
		RETP	;------------------ DONE

;------------------------------------------------
; Recieve one byte from mouse
;------------------------------------------------

ReceiveByte	MOV	BitCount, #8		;sets the number of bits to receive
		BANK	Rxbytes
		CLR	RxTemp
		JB	ClockLine, $		;wait for the clockline to go low
		JB	DataLine, $		;wait for the dataline to go low, this is start bit
:RxLoop		JNB	ClockLine, $		;wait for clockline to go high
		JB	Clockline, $		;wait for clockline to go low
		MOVB	C, DataLine		;get next bit and store it in carry flag
	;	SNB	C			;if the bit received was high, ParityCount is increased
	;	  INC	ParityCount		;  high bits are counted for parity check
		RR	RxTemp			;store received bit into RxByte
		DECSZ	BitCount		;decrease BitCount, if it equals zero, byte has been received
		  JMP	:RXLoop			;if BitCount is > 0 loop again
		JNB	ClockLine, $		;wait for clockline to go high
		JB	Clockline, $		;wait for clockline to go low
	;	MOVB	C, DataLine		;receive parity bit, used for parity check..not implemented yet
		JNB	ClockLine, $		;wait for clockline to go high 
		JB	Clockline, $		;wait for clockline to go low, stop bit
		JNB	ClockLine, $		;end of byte, wait for clockline to go high
		CLR	ParityBit
		CLR	ParityCount
		RETP	;------------------ DONE
	
;------------------------------------------------
; misc subroutines
;------------------------------------------------
Delay100	BANK	Counters		;delay 100(ish) microseconds. the delay is padded a bit
		MOV	Counter1, #110		;and can be set longer but it must NOT be set lower than
		DJNZ	Counter1, $		;100 microseconds. it is required for sending commands
						;to the mouse
		RETP	;------------------ DONE

;------------------------------------------------
; Initialize Mouse
;	Attempts to intialize mouse with 3 buttons, scroll wheel, and 2 axis. The mouse
;	must have a scroll wheel for this to happen. If not, mouse will be intiallized 
;	as standard PS/2 mouse (2 buttons, 2 axis) NOTE: Standard 2-button mice have not
;	been tested yet and further changes may need to be made to the code in order for
;	them to work correctly.
;------------------------------------------------
InitializeMouse	CLR	Flags
		MOV	Txbyte, #$FF		;send reset
		CALL	Sendbyte
		CALL	ReceiveByte		;acknowledge byte from mouse...ignor it
		CALL 	ReceiveByte		;bat completion from mouse...ignor it
		CALL	ReceiveByte		;mouse id from mouse...ignor it
		;------------------------<|start of scroll wheel check
		MOV	TxByte, #$F3	;<| send set sample rate change
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$C8	;<| send sample rate 200 
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$F3	;<| send set sample rate change
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$64	;<| send sample rate 100
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$F3	;<| send set sample rate change
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$50	;<| send sample rate 80
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse...ignor it
		MOV	TxByte, #$F2	;<| get device type
		CALL	SendByte	;<|
		CALL	ReceiveByte	;<| acknowledge byte from mouse... ignor it
		CALL	ReceiveByte	;<| device ID from mouse, 00 = 2-buttons, 03 = 3-button & scroll wheel
		;------------------------<|end of scroll wheel check
		MOV	TxByte, #$E8		;set resolution
		CALL	SendByte
		CALL	ReceiveByte		;acknowledge byte from mouse...ignor it
		MOV	TxByte, #$03		;set resolution to 8 counts/mm
		CALL	SendByte
		CALL	ReceiveByte		;acknowledge byte from mouse...ignor it
		MOV	TxByte, #$F3		;send set sample rate change
		CALL	SendByte
		CALL	ReceiveByte		;acknowledge byte from mouse...ignor it
	;	MOV	TxByte, #$28		;set sample rate 40	slow
	;	MOV	TxByte, #$50		;set sample rate 80	not as slow
	;	MOV	TxByte, #$64		;set sample rate 100	default
	;	MOV	TxByte, #$C8		;set sample rate 200	full throttle
		CALL	SendByte
		CALL	ReceiveByte		;acknowledge byte from mouse.. ignor it
		MOV	TxByte, #$F4		;send enable data reporting, this is required for...
		CALL	SendByte		;  STREAMING MODE
		CALL	ReceiveByte		;acknowledge byte from mouse.. ignor it
		MOV	TxByte, #$F0	;<| send command to enter REMOTE MODE. to setup mouse for
		CALL	SendByte	;<| STREAMING MODE comment out these three lines
		CALL	ReceiveByte	;<|
		RETP	;------------------ DONE

;------------------------------------------------
; Get data packet from mouse
;	By default this will fetch a 4-byte data packet, which is needed for
;	mice that have 3 buttons and a scroll whell. A 2-button mouse will
;	send a 3-byte data packet. If using a 2-button mouse, comment out the
;	last 2 lines (before the RETP instruction).
;------------------------------------------------
GetData		BANK	RxBytes
		CALL	ReceiveByte		;get first byte in data packet
		MOV	RxByte1, RxTemp		;store byte in RxByte1
		CALL	ReceiveByte		;get second byte in data packet
		MOV	RxByte2, RxTemp		;store byte in RxByte2
		CALL	ReceiveByte		;get third byte in data packet
		MOV	RxByte3, RxTemp		;store byte in RxByte3
		CALL	ReceiveByte	;<|get forth byte in data packet (if it exists)
		MOV	RxByte4, RxTemp	;<|store byte in byte4
		RETP	;------------------ DONE

;------------------------------------------------
;   MAIN LOOP
;------------------------------------------------
MAIN		CALL 	InitializeMouse
:Loop		MOV	TxByte, #$EB	;<|send READ DATA request to mouse. this is required to get...
		CALL	SendByte	;<|  data packets when the mouse is setup for REMOTE MODE. 
		CALL	ReceiveByte	;<|acknowledge byte from mouse...ignor it
						;to use the poormans fix below for STREAMING MODE, comment
						;out the 3 lines line above.

	;	JB	ClockLine, $		;This is the poorman's fix for not having an inturrupt routine
						;for using the mouse in STREAMING MODE. In streaming mode, the 
						;start of an incoming data packet is the mouse bringing the 
						;Clock Line low. This simply loops until that happens. To use
						;the poormans fix, comment out the first 3 lines the main 
						;program :loop, and comment out the last 3 lines of 
						;InitializeMouse subroutine, and un-comment this line.

		CALL	GetData			;gets the data packet from the mouse, used for REMOTE and...
						;  STREAMING MODE
		
	;[INSERT CODE HERE TO DO SOMETHING USEFUL WITH MOUSE DATA]

		JMP	:Loop