;--------------------------------------------------------------------------------------------- 
; CLOCK/TIMING INFORMATION: 
;--------------------------------------------------------------------------------------------- 
; 
;   PS/2 bus clock low time =  40 us +/- 25% (30 us - 50 us) 
;   PS/2 bus clock high time = 40 us +/- 25% (30 us - 50 us) 
;   RC osc @ 20pF/5k = 4.61 MHz +/- 25% (3.50 MHz - 5.76 MHz) 
;   1 instruction cycle @ 4.61 MHz (RC) = 0.87 us +/- 25% (0.65 us - 1.09 us) 
;   Optimum PS/2 bus clock low time @4.61MHz = 45.97 instruction cycles 
;   Actual PS/2 bus clock low time = 46 instruction cycles 
;   Actual PS/2 bus clock low time @4.61MHz (RC) = 40.0us +/- 25% (30us-50us) 
;   Actual PS/2 bus clock frequency @461MHz (RC) = 12.5 kHz +/- 25% (10.0kHz-16.7kHz) 
;--------------------------------------------------------------------------------------------- 
;       HEADER: 
;--------------------------------------------------------------------------------------------- 
  TITLE       "PS/2 Device Routines" 
  SUBTITLE    "By Adam Chapweske" 
  LIST        P=16F84 
  INCLUDE     "p16f84.inc" 
  RADIX       DEC  
  ERRORLEVEL  -224, 1 
  __CONFIG    _CP_OFF & _WDT_OFF & _RC_OSC 
  

;--------------------------------------------------------------------------------------------- 
;      DEFINES: 
;--------------------------------------------------------------------------------------------- 
#DEFINE DATA  PORTB, 7 
#DEFINE CLOCK PORTB, 6 

;--------------------------------------------------------------------------------------------- 
; RAM ALLOCATION: 
;--------------------------------------------------------------------------------------------- 
  cblock 
        TEMP0          
        RECEIVE 
        PARITY 
        COUNTER 
  endc
 



;---------------------------------------------------------------------------------------------
;Required Routines & Macros: 
;--------------------------------------------------------------------------------------------- 
;       MACROS: 
;--------------------------------------------------------------------------------------------- 
Delay   macro   Time            ;Delay "Cycles" instruction cycles 
  if (Time==1) 
        nop 
        exitm 
  endif 
  if (Time==2) 
        goto $ + 1 
        exitm 
  endif 
  if (Time==3) 
        nop 
        goto $ + 1 
        exitm 
  endif 
  if (Time==4) 
        goto $ + 1 
        goto $ + 1 
        exitm 
  endif 
  if (Time==5)  
        goto $ + 1 
        goto $ + 1 
        nop 
        exitm 
  endif 
  if (Time==6) 
        goto $ + 1 
        goto $ + 1 
        goto $ + 1 
        exitm 
  endif 
  if (Time==7) 
        goto $ + 1 
        goto $ + 1 
        goto $ + 1 
        nop 
        exitm 
  endif 
  if (Time%4==0) 
        movlw (Time-4)/4 
        call Delay_Routine 
        exitm 
  endif 
  if (Time%4==1) 
        movlw (Time-5)/4 
        call Delay_Routine 
        nop 
        exitm 
  endif 
  if (Time%4==2) 
        movlw (Time-6)/4 
        call Delay_Routine 
        goto $ + 1 
        exitm 
  endif 
  if (Time%4==3) 
        movlw (Time-7)/4 
        call Delay_Routine 
        goto $ + 1 
        nop 
        exitm 
  endif 
  endm 

;--------------------------------------------------------------------------------------------- 
; DELAY: 
;--------------------------------------------------------------------------------------------- 
;Delays 4w+4 cycles (including call,return, and movlw) (0=256) 
Delay_Routine   addlw   -1              ;Precise delays used in I/O 
                btfss   STATUS, Z  
                goto    Delay_Routine 
                return  
 
 



;--------------------------------------------------------------------------------
;ByteOut: 
;Sends a byte in w to the host.  Returns 0xFE if inhibited during transmission.  Returns 0xFF if host interrupts to send its own data.  Returns 0x00 if byte sent successfully.
;--------------------------------------------------------------------------------------------- 
; OUTPUT ONE BYTE:  - TIMING IS CRITICAL!!! 
;--------------------------------------------------------------------------------------------- 
ByteOut         movwf   TEMP0 
InhibitLoop     btfss   CLOCK           ;Test for inhibit (si la ligne CLOCK est à 1 on sort de la boucle InhibitLoop) 
                goto    InhibitLoop 
                Delay   50 		;attente
                btfss   CLOCK		;nouveau test 
                goto    InhibitLoop	;la ligne est à zéro 
                btfss   DATA            ;Check for request-to-send (test PORTB,7)
                retlw   0xFF 		;la sous routine retourne FF (le PC(maître) a mis la ligne à 0
					; pour envoyer une donnée)
                clrf    PARITY 		;la ligne est à l'état haut et accepte l'envoi, le bit de parité est mis à zéro)
                movlw   0x08		;Nombre de bits à envoyer en w 
                movwf   COUNTER		;mis en mémoire 
                movlw   0x00		;0 dans w   
                call    BitOut          ;Start bit (0) envoi du start bit
                btfss   CLOCK           ;Test for inhibit on teste si la ligne est toujours disponible 
                goto    ByteOutEnd 	;si la ligne n'est pas libre on va à la sous routine de fin de byte
                Delay   4 		;attente
                			;sous routine d'émission d'un octet
ByteOutLoop     movf    TEMP0, w	;TEMP0 en w		
                xorwf   PARITY, f 	;OU exclusif (0 si 1 et 1) entre w et f, résultat en f
                			;la première fois PARITY est à O et XOR avec w ==> w en f
					;puis TEMPO est décalé à Dte.
                			;le XOR avec w correspond à une addition avec report à chaque boucle dont le résultat
                			;est stocké dans PARITY. La fonction BitOut envoie le bit 0 (pair ou impair)
					;de cet octet
                call    BitOut          ;sortie d'un bit (paramètre en w)
                btfss   CLOCK           ;Test for inhibit 
                goto    ByteOutEnd 	;si la ligne n'est pas libre on va à la sous routine de fin de byte
                rrf     TEMP0, f	;rotation à droite de TEMPO à travers la carry 
                decfsz  COUNTER, f	;decrémentation du compteur et saut de l'instruction suivante si zéro 
                goto    ByteOutLoop 	;on n'a pas encore envoyé tout l'octet
                Delay   2 		;l'octet a été envoyé
                comf    PARITY, w	;complémentation du bit de parité, résultat dans w  
                call    BitOut          ;Parity bit: envoi du bit de parité inversé 
                btfss   CLOCK           ;Test for inhibit 
                goto    ByteOutEnd 	;si la ligne n'est pas libre on va à la sous routine de fin de byte
                Delay   5				;la ligne est libre, attente 
                movlw   0xFF		;FF en w    
                call    BitOut          ;envoi Stop bit (1) 
                Delay   48 
                retlw   0x00		;Returns 0x00 if byte sent successfully 

ByteOutEnd      bsf     STATUS, RP0	;sélectionne banque 1 
                bsf     DATA 		;met PORTB, 7 à 1
                bsf     CLOCK		;met PORTB, 6 à 1 
                bcf     STATUS, RP0	;sélectionne banque 0 
                retlw   0xFE		;Returns 0xFE if inhibited during transmission 
   
BitOut          bsf     STATUS, RP0	;sélectionne banque 1		 
                andlw   0x01		;and 01 et w affecte Z 
                btfss   STATUS, Z 	;teste Z du status, saute l'instruction suivante si Z=1
                bsf     DATA		;met PORTB, 7 à 1
                btfsc   STATUS, Z	;teste Z du status, saute l'instruction suivante si Z=0
                bcf     DATA		;met PORTB, 7 à 0 
                Delay   21		;attente CLOCK à l'état haut pour respecter le timing d'envoi
                bcf     CLOCK		;met CLOCK à 0 (signal d'horloge état bas)
                Delay   45		;attente CLOCK à l'état bas
                bsf     CLOCK		;met CLOCK à 1 
                bcf     STATUS, RP0	;sélectionne banque 0 
                Delay   5 
                return
 



;--------------------------------------------------------------------------------------------
;ByteIn: 
;Reads a byte from the host.  Result in "RECEIVE" register.  Returns 0xFE in w if host aborts transmission.  
;Returns 0xFF in w if framing/parity error detected.  Returns 0x00 in w if byte received successfully.
;--------------------------------------------------------------------------------------------- 
; READ ONE BYTE: - TIMING IS CRITICAL!!! 
;--------------------------------------------------------------------------------------------- 
ByteIn          btfss   CLOCK           ;Wait for start bit 
                goto    ByteIn 
                btfsc   DATA		;Teste l'état de la ligne et saute l'instruction suivante si 0 (start bit)
                goto    ByteIn 
                movlw   0x08		;intialisation du compteur et du bit de parité
                movwf   COUNTER 
                clrf    PARITY 
                Delay   28 
ByteInLoop      call    BitIn           ;Data bits (au retour le bit correspondant est en w (00 ou 80)
                btfss   CLOCK           ;Test for inhibit saute l'instruction suivante si CLOCK à 1
                retlw   0xFE		;transmission avortée 
                bcf     STATUS, C	;Carry à 0 dans status 
                rrf     RECEIVE, f	;Rotation à Dte avec Carry de l'octet RECEIVE en mémoire
                			;le LSB (Bit 0)est reçu en premier 
                iorwf   RECEIVE, f	;OU inclusif entre w et RECEIVE résultat en f
                			;vient ajouter 1 bit de poids supérieur à chaque fois 
                xorwf   PARITY,f	;addition avec report dans PARITY 
                decfsz  COUNTER, f	;décrémentation du compteur et sortie de la boucle quand Z=0 
                goto    ByteInLoop 
                Delay   1  
                call    BitIn           ;Parity bit (réception du bit de parité)
                btfss   CLOCK           ;Test for inhibit 
                retlw   0xFE		;transmission avortée 
                xorwf   PARITY, f	;OU exclusif avec PARITY, résultat en f 
                Delay   5
                
                	;Acquisition du Stop bit 
ByteInLoop1     Delay   1 
                call    BitIn           ;Stop bit (DATA=1 ==> w=80)
                btfss   CLOCK           ;Test for inhibit 
                retlw   0xFE 		;transmission avortée
                xorlw   0x00		;OU exclusif w avec 00 
                btfsc   STATUS, Z	;si résultat=0 sauter l'instruction suivante 
                clrf    PARITY 		;PARITY=0 (le stop bit a été détecté, on réinitialise PARITE pour la réception suivante)
                btfsc   STATUS, Z       ;Stop bit=1? oui on sort de la boucle 
                goto    ByteInLoop1     ;No--keep clocking.
                 
					;Acknowledge (accusé de réception des données)
                bsf     STATUS, RP0     ;sélection banque 1
                bcf     DATA 		;DATA = 0
                Delay   11 
                bcf     CLOCK		;CLOCK = 0
                Delay   45 
                bsf     CLOCK 		;CLOCK = 1
                Delay   7 
                bsf     DATA		;DATA = 1
                bcf     STATUS, RP0	;sélection banque 0 

                btfss   PARITY, 7       ;Parité correcte? (test sur le bit 7) saute l'instruction suivante si oui
                retlw   0xFF            ;No--return error 

                Delay   45 
                retlw   0x00		;Returns 0x00 in w if byte received successfully 

BitIn           Delay   8 
                bsf     STATUS, RP0	;sélectionne banque 1
                bcf     CLOCK 		;met la CLOCK à 0
                Delay   45 
                bsf     CLOCK		;met la CLOCK à 1 
                bcf     STATUS, RP0	;sélectionne banque 0	 
                Delay   21 
                btfsc   DATA		;teste la ligne DATA saute si 0 
                retlw   0x80		;w=80h si DATA=1
                retlw   0x00		;w=00h si DATA=0
 

end