; RA0 = PS/2 Data
; RA1 = PS/2 Clock
; RC0 = Motore 1
; RC1 = Motore 1
; RC2 = Motore 2
; RC3 = Motore 2
; RB0 = D4 \
; RB1 = D5 |	
; RB2 = D6  \ Display
; RB3 = D7  / Lcd
; RB4 = RS |
; RB5 = CS /

	PROCESSOR 16F870
	RADIX DEC 
        __CONFIG   h'3F30'

        INCLUDE "P16F870.inc"


BYTEIN EQU 0x20
BYTEOUT	EQU 0x21
BYTE1 EQU 0x22
BYTE2 EQU 0x23
BYTE3 EQU 0x24
SWITCH EQU 0x25
LOOP EQU 0x26
CLKCNT EQU 0x27
STORE1 EQU 0x28
RSLINE EQU 0x29
LOOPA EQU 0x2A
STORE EQU 0x2B
PARITY EQU 0x2C
PARITYBIT EQU 0x2D
STOPBIT	EQU 0x2E
STARTBIT EQU 0x2F


                ORG 0
                goto	START
                ORG 4
                goto	START

TABLCD
	addwf	PCL, F	; tabella di inizializzazione del display
	retlw	0x28	; interfaccia a 4 bit, 2 righe, catattere 5x7
	retlw	0x28
	retlw	0x28
	retlw	0x28
	retlw	0x06	; cursore a incremento, posizione scritte bloccate
	retlw	0x0C	; display acceso, cursore invisibile
	retlw	0x01	; ripulisci display
	retlw	0x01
                 
;***************************************;
; Rappresentazione grafica dei pulsanti ;
;***************************************;
BUTTON
	movf	SWITCH, W
	andlw	0x07		; mantieni solo i primi 3 bit (quelli dei pulsanti)
	addwf	PCL, F
	goto	NONE
	goto	LEFT
	goto	RIGHT
	goto	BOTH
	goto	MIDDLE
	goto	MLEFT
	goto	MRIGHT
	goto	THREE
NONE
	movlw	0x2E		; '.' simbolo pulsante non premuto
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	goto	FINEPULS
LEFT
	movlw	0x4F		; 'O' simbolo pulsante premuto
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	goto	FINEPULS
RIGHT
	movlw	0x2E
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	goto	FINEPULS
BOTH
	movlw	0x4F
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	goto	FINEPULS
MIDDLE
	movlw	0x2E
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	goto	FINEPULS
MLEFT
	movlw	0x4F
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	movlw	0x2E
	call	LCDOUT
	goto	FINEPULS
MRIGHT
	movlw	0x2E
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	goto	FINEPULS
THREE
	movlw	0x4F
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
	movlw	0x4F
	call	LCDOUT
FINEPULS
	return

;*******************;
; Punto di partenza ;
;*******************;
START
	movlw	0xFF		
	movwf	PORTA
	clrf	PORTB
	clrf	PORTC
	clrf	CLKCNT

	bsf	STATUS, RP0	; Bank1
	movlw	0x06
	movwf	ADCON1		; PORTA tutti ingressi digitali
	clrf	TRISB		; PORTB tutti output
	clrf	TRISC		; PORTC tutti output
	movlw	0xFF
	movwf	TRISA		; PORTA tutti input
	movlw	0x87		; PORTB pullups off, prescaler 1:256 assegnato al timer0
	movwf	OPTION_REG
	bcf	STATUS, RP0	; Bank0

SETUP
	movlw	0x32
	movwf	CLKCNT
	call	DEL_10		; 0,5 s di ritardo
	bcf	PORTA, 1
	bsf	STATUS, RP0
	bcf	TRISA, 1	; linea data come output per un po'.
	bcf	STATUS, RP0	;	questo permette ai mouse con doppia interfaccia (ps/2 - usb)
				;	di commutare sul protocollo ps/2
	clrf	LOOP		; ciclo di init dell' lcd
	clrf	RSLINE		; linea RS a 0 per l'invio comandi
LCDSET
	movf	LOOP,W		; ottieni l'indirizzo della tabella
	call	TABLCD		; ottieni l'istruzione dalla tabella
	call	LCDOUT		; eseguila
	incf	LOOP, F		; incrementa loop
	btfss	LOOP, 3		; e' stata eseguita l'ultima istruzione?
	goto	LCDSET		; no
	movlw	0x32
	movwf	CLKCNT
	call	DEL_10		; 0,5 s di ritardo
      	bsf	STATUS, RP0
	bsf	TRISA, 1	; rilevamento automatico del mouse ormai avvenuto (si spera!)
	bcf	STATUS, RP0
	         
	bsf	RSLINE, 4	; linea RS a 1 per invio caratteri
	movlw	0x32
	movwf	CLKCNT
	call	DEL_10		; 0,5 s di ritardo
		
	movlw	0xFF		; comando RESET
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA	; leggiamo le risposte ignorandole
	call	RECEIVEDATA
	call	RECEIVEDATA

	movlw	0xF5		; DATA REPORTING ENABLE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0xF3		; SAMPLING RATE CHANGE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0x0A		; SAMPLING RATE VALUE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0xE8		; SET RESOLUTION
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0x03		; RESOLUTION VALUE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0xF0		; SET REMOTE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	movlw	0xF4		; ENABLE
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA

	call	LCD1		; prima riga, primo carattere
	movlw	'L'
	call	LCDOUT
	movlw	'M'
	call	LCDOUT
	movlw	'R'
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	movlw	'X'
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	movlw	'Y'
	call	LCDOUT
MAIN

	movlw	0xEB		; READ DATA
	movwf	BYTEOUT
	call	SENDCOMMAND

	call	RECEIVEDATA	; leggi il primo byte ignorandolo

	call	RECEIVEDATA	; ricevi il byte contenente tutti i flag
	movf	BYTEIN, W
	movwf	BYTE1
	movwf	SWITCH		; memorizzalo nel registro SWITCH

	call	RECEIVEDATA	; ricevi il byte dello spostamento sull'asse X
	movf	BYTEIN, W
	movwf	BYTE2

	call	RECEIVEDATA	; ricevi il byte dello spostamento sull'asse Y
	movf	BYTEIN, W
	movwf	BYTE3


	call	LCD21		; seconda riga, primo carattere

	call	BUTTON		; visualizza lo stato dei pulsanti

	movlw	' '
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	movlw	' '
	call	LCDOUT

	btfsc	BYTE2, 0
	goto	DXSX
	movlw	' '
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	bcf	PORTC, 0
	nop
	bcf	PORTC, 1
	goto	VERT
DXSX
	btfsc	SWITCH, 4
	goto	SINISTRA
	movlw	0xC7		; freccia a destra
	call	LCDOUT
	movlw	' '
	call	LCDOUT
	btfss	SWITCH, 0
	goto	VERT		; se non e' premuto il testo sinistro non muovere i motori
	bcf	PORTC, 1
	nop
	bsf	PORTC, 0
	goto	$+1
	nop
	goto	$+1
	goto	VERT
SINISTRA
	movlw	0xC8		; freccia a sinistra
	call	LCDOUT		; visualizza il segno del movimento sull'asse X
	movlw	' '
	call	LCDOUT
	btfss	SWITCH, 0
	goto	VERT		; se non e' premuto il testo sinistro non muovere i motori
	bcf	PORTC, 0
	nop
	bsf	PORTC, 1
	goto	$+1
	nop
	goto	$+1
VERT
	btfsc	BYTE3, 0
	goto	UPDW
	movlw	' '
	call	LCDOUT
	bcf	PORTC, 2
	nop
	bcf	PORTC, 3
	goto	RICOMINCIA
UPDW
	btfsc	SWITCH, 5
	goto	GIU
	movlw	0xC5		; freccia su
	call	LCDOUT
	btfss	SWITCH, 1
	goto	RICOMINCIA	; se non e' premuto il testo sinistro non muovere i motori
	bcf	PORTC, 2
	nop
	bsf	PORTC, 3
	goto	$+1
	nop
	goto	$+1
	goto	RICOMINCIA
GIU
	movlw	0xC6		; freccia giu
	call	LCDOUT		; visualizza il segno del movimento sull'asse Y
	btfss	SWITCH, 1
	goto	RICOMINCIA	; se non e' premuto il testo sinistro non muovere i motori
	bcf	PORTC, 3
	nop
	bsf	PORTC, 2
	goto	$+1
	nop
	goto	$+1
RICOMINCIA
	goto	MAIN		; ripeti il ciclo

;***************************;
; INVIA IL COMANDO AL MOUSE ;
;***************************;

SENDCOMMAND
	clrf	PARITY		; azzera il registro per la parita
	bcf	PORTA, 1	; abbassa il clock per almeno 100us (1)
	bsf	STATUS, RP0	; Bank1
	bcf	TRISA, 1	; linea clock come uscita
	bcf	STATUS, RP0	; Bank0

	call	DEL100US	; ritardo di 100us

	bcf	PORTA, 0	; abbassa la linea data (START bit)  (2)
	bsf	STATUS, RP0	; Bank1
	bcf	TRISA, 0	; linea data come output
	nop
	bsf	TRISA, 1	; linea clock come input (3)
	bcf	STATUS, RP0	; Bank0

	btfsc	PORTA, 1	; attendi che il clock vada basso (4)
	goto	$-1

	movlw	0x08		; init registro LOOP con il numero di bit da inviare
	movwf	LOOP

SENDLOOP
	btfss	BYTEOUT, 0	; controllo del bit da inviare (meno significativo)
	goto	OUTZERO
	goto	OUTUNO
OUTZERO
	bcf	PORTA, 0
	bsf	STATUS, RP0
	bcf	TRISA, 0
	bcf	STATUS, RP0	; invia uno 0 (5)
	goto	PROSSIMO
OUTUNO
	bsf	STATUS, RP0	; Bank1
	bsf	TRISA, 0	; invia un 1 (5)
	bcf	STATUS, RP0	; Bank0
PROSSIMO
	movf	BYTEOUT, W	; recuperiamo il bit inviato per calcolare quello di parita'
	andlw	0x01
	addwf	PARITY, F	; aggiungi al contatore PARITY

	btfss	PORTA, 1	; attendi che il clock vada alto (6)
	goto	$-1

	btfsc	PORTA, 1	; attendi che il clock vada basso (7)
	goto	$-1

	rrf	BYTEOUT, F	; ruota a destra il byte da inviare
	decfsz	LOOP, F
	goto	SENDLOOP		; ripeti fino alla fine di tutti gli 8 bit (8)
		
	btfsc	PARITY, 0	; controllo bit di parita' (complementato)
	goto	PARUNO
	bsf	STATUS, RP0
	bsf	TRISA, 0
	bcf	STATUS, RP0	; invia uno 0
	goto	FINEPAR
PARUNO
	bcf	PORTA, 0
	bsf	STATUS, RP0
	bcf	TRISA, 0	; invia un 1
	bcf	STATUS, RP0
FINEPAR
	btfss	PORTA, 1	; attendi che il clock vada alto (6)
	goto	$-1
	btfsc	PORTA, 1	; attendi che il clock vada basso (7)
	goto	$-1

	bsf	STATUS, RP0	; Bank1
	bsf	TRISA, 0	; linea data come input (9)
	bcf	STATUS, RP0	; Bank0

	btfsc	PORTA, 0	; attendi che la linea data vada bassa (10)
	goto	$-1
	btfsc	PORTA, 1	; attendi che il clock vada basso (11)
	goto	$-1

	btfss	PORTA, 1	; attendi che il clock torni alto (12)
	goto	$-1
	btfss	PORTA, 0	; attendi che la linea data torni alta
	goto	$-1
		
	return

;***********************;
; Ricevi dati dal mouse ;
;***********************;
RECEIVEDATA
	clrf	BYTEIN
	clrf	PARITY
	bsf	STATUS, RP0	; Bank1
	movlw	0xFF		; data e clock come input
	movwf	TRISA
	bcf	STATUS, RP0	; Bank0

WAITSTARTBIT
	comf	PORTA, W	; attendi che data e clock vadano alte
	andlw	0x03
	btfss	STATUS, Z
	goto	WAITSTARTBIT

	btfsc	PORTA, 1	; attendi che il clock vada basso
	goto	$-1
	movf	PORTA, W
	andlw	0x01
	movwf	STARTBIT	; ricevi il bit di start
	btfss	PORTA, 1	; attendi che il clock vada alto
	goto	$-1
	btfsc	STARTBIT, 0	; lo start bit e' 0?
	goto	WAITSTARTBIT	; no, torna indietro

	movlw	0x08		; yes
	movwf	LOOP
	clrf	PARITY

RECEIVELOOP
	btfsc	PORTA, 1	; attendi che il clock vada basso
	goto	$-1
	rrf	PORTA, W	; salva il bit sulla linea data nel Carry flag
	rrf	BYTEIN, F	; ruota a destra il byte ricevuto

	btfsc	PORTA, 0	; se il bit 0 e' alto sommalo al contatore di parita'
	incf	PARITY, F

	btfss	PORTA, 1	; attendi che il clock vada alto
	goto	$-1
	decfsz	LOOP, F		; sono stati ricevuti tutti gli 8 bit?
	goto	RECEIVELOOP	; no, continua

	btfsc	PORTA, 1	; attendi che il clock vada basso
	goto	$-1
	movf	PORTA, W	; ricevi il bit di parita'
	andlw	0x01
	movwf	PARITYBIT	; bit di parita'. Non lo usiamo ma bisogna leggerlo
	btfss	PORTA, 1	; attendi che il clock vada alto
	goto	$-1

	btfsc	PORTA, 1	; attendi che il clock vada basso
	goto	$-1
	movf	PORTA, W	; ricevi il bit di stop
	andlw	0x01
	movwf	STOPBIT		; bit di stop. Non lo usiamo ma bisogna leggerlo

	return

;***************************************************************;
; Queste routine impostano la posizione del cursore sul display ;
;***************************************************************;
LCD1
	movlw	0x80	; prima riga, primo carattere
	goto	LCDLIN

LCD21
	movlw	0xC0	; seconda riga, primo carattere
	goto	LCDLIN

LCDLIN
	bcf	RSLINE, 4	; linea RS a 0 per invio comandi
	call	LCDOUT		; invia comando
	bsf	RSLINE, 4	; linea RS a 1
	return


;****************************;
; Routine invio dati LCD     ;
; invia all'LCD il byte in W ;
;       Ricorsiva!!          ;
;****************************;
LCDOUT
	movwf	STORE1		; memorizza il byte da inviare
	movlw	0x3C		; contatore di ritardo per temporizzazione lcd
	movwf	LOOPA
DELAY
	decfsz	LOOPA, F
	goto	DELAY
	call	SENDIT		; invia prima il MSB poi il LSB

SENDIT
	swapf	STORE1, F	; inverti i 2 nibble
	movf	STORE1, W	; 
	andlw	0x0F		; isola il nibble piu significativo (poi il meno significativo)
	iorwf	RSLINE, W	; imposta la linea RS
	movwf	PORTB		; invia il dato sulla porta B
	nop
	bsf	PORTB, 5	; attiva enable
	nop
	bcf	PORTB, 5	; disattiva enable
	return			; la prima volta che si arriva a questa istruzione, il programma
				; ritorna alla label SENDIT, cioe' subito dopo la call in questa
				; routine. La seconda volta invecie ritorna all' istruzione dopo
				; 'call LCDOUT' nella routine che ha invocato l'invio del byte.

;********************;
; Routine di ritardo ;
;********************;
DEL_10
	bcf	STATUS, RP0	; Bank0
	bcf	INTCON, 2	; azzera interrupt flag del timer0
	movlw	0xD8
	movwf	TMR0		; 0xD8 in TMR0 (overflow ogni 10ms circa)
DEL10
	btfss	INTCON, 2	; controlla se il timer0 e' andato in overflow
	goto	DEL10
	decfsz	CLKCNT, F	; decrementa il numero dei cicli e ricomincia
	goto	DEL_10
	return

DEL100US
	movlw	0x64
	movwf	CLKCNT
DEL100
	decfsz	CLKCNT, F
	goto	DEL100
	return

END
