;**********************************************************************
; *
; Filename: CheatBox.asm *
; Date: 07/01/2007 *
; File Version: 0.1 *
; *
; Author: Rubén Arranz *
; Company: Electronics Design *
; *
; *
;**********************************************************************
; *
; Files required: *
; 16F648A.lkr *
; *
; *
;**********************************************************************
; *
; Notes: *
; *
; *
; *
; *
;**********************************************************************
list      p=16F648A           ; list directive to define processor
#include <p16F648A.inc>       ; processor specific variable definitions
errorlevel  -302              ; suppress message 302 from list file
__CONFIG   _CP_OFF & _DATA_CP_OFF & _LVP_OFF & _BOREN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _RC_OSC_NOCLKOUT
;Variables Tenemos disponibles desde 0x20 hasta 0x7F en el Banco 0
;Mirar Datasheet para los otros bancos de memoria.
BITTECLADO equ 0x20 ;Bits faltantes por leer del Teclado
TDATA equ 0x21 ;Variable para almacenar el dato recibido
TPARIDAD equ 0x22 ;Variable intermedia de control de paridad
TGEN equ 0x23 ;Variable de proposito general asociada al teclado
TSTATUS equ 0x24 ;Esta Variable tendra varios FLAGS para indicarnos el estado de lectura del Teclado
;*** Como un dato puede estar compuesto por 3 Bytes, Vamos a Guardarlos en las siguientes tres variables
TDATA1 equ 0x25 ;Primer parte del Dato
TDATA2 equ 0x26 ;Segunda parte del Dato
TDATA3 equ 0x27 ;Tercera parte del Dato
CSTATUS equ 0x28 ;Esta Variable tendra varios FLAGS para indicarnos el estado de lectura del Computador
;Constantes
CCK equ H'0000' ;Clock Hacia el computador
CDT equ H'0001' ;Linea de Datos desde y Hacia el Computador
TCK equ H'0002' ;Clock del Teclado
TDT equ H'0003' ;Linea de Datos desde y Hacia el Teclado
;*** Los siguientes son los Flags de TSTATUS
T_PAR equ H'0000' ;Indica el estado de la paridad del teclado
T_WAIT equ H'0001' ;Indica un estado de espera mientras dura el estado bajo del clock
T_EN equ H'0002' ;Indica si el teclado está inhibido de recibir datos o no
T_CKHI equ H'0003' ;Este FLAG nos dirá quién puso en BAJO el Clock del Computador
;*** Los siguientes son los Flags de CSTATUS
C_PAR equ H'0000' ;Indica el estado de la paridad del teclado
C_WAIT equ H'0001' ;Indica un estado de espera mientras dura el estado bajo del clock
C_EN equ H'0002' ;Indica si el teclado está inhibido de recibir datos o no
C_CKHI equ H'0003' ;Este FLAG nos dirá quién puso en BAJO el Clock del Teclado
C_DTHI equ H'0004' ;Este FLAG nos dirá quién puso en BAJO el Clock del Teclado
;**********************************************************************
RESET_VECTOR CODE 0x000 ; Vector de Reset del Procesador
goto    Rst              ; Vamos al inicio del Programa
MAIN CODE 0x004
;*** Tras un reset, lo primero que hacemos es cargar la configuración grabada en memoria.
;*** Desactivamos toda rutina y ejecutaremos el Bucle Principal
; -Leer Teclado y host (Todo lo que implica) y almacenar en variables correspondientes.
; -Verificar si se intenta entrar en AutoProgramación y saltar allí en caso de ser así.
; -Verificar activación/desactivación de algún "Combo" y realizar según el caso
; -Enviar en forma transparente los datos desde y hacia el Host y desde y hacia el Teclado
; -Si hay combos para enviar al host, inhibir el teclado y enviar los datos al host.
;*** Rutina principal
Rst
bsf STATUS,RP0 ;Me cambio al Banco de memoria 1
bcf OPTION_REG,NOT_RBPU ;Activo las resistencia Pull-Up del Port B
bcf STATUS,RP0 ;Vuelvo al viejo y querido Banco de memoria 0
movlw 0x86 ;Direcciono TRISB en Forma Indirecta, para no tener...
movwf FSR ;... Que cambiar de Banco a Cada Rato.
;*** Para Tener en cuenta:
;*** La Forma de leer y escribir el puerto será diferente de lo normal, ya que los puertos...
;*** ...deberán actuar como de lectura y escritura, la solución es solo actuar como salidas...
;*** ...cuando queremos poner un "cero", y cuando queremos poner un "uno", le pedimos al port...
;*** ...que se convierta en entrada, dejando que la resistencia pull-up haga el trabajo...
;*** ...y nosotros podremos detectar si del otro lado alguien pone un "cero".
;*** Resumiendo poner un 0 implica TRISB_X -> 0 (Convertirlo en Salida) / PORTB_X -> 0
;*** Poner un 1 Implica PORTB_X -> 1 / TRISB_X -> 1 (Convertirlo en Entrada)
Inicio
call LeerTeclado
call LeerCompu
goto Inicio
;*** La idea de esta rutina es la siguiente:
;*** Primero que nada debemos pasar la información del teclado al computador, con la mínima demora.
;*** No seremos agentes retendores de datos, el circuito intentará ser transparente para el teclado
;*** y el computador, solo "espiaremos" los datos para tomar acciones y en los silecios que ocurran
;*** entre ambos, enviar al host las teclas que correspondan.
LeerTeclado
btfss PORTB,TDT ;Lo primero entonces es pasar el dato...
goto Poner0 ;... al computador
bsf PORTB,CDT ;Si el dato es un 1 ponemos un 1...
bsf INDF,CDT ;... Convertimos el puerto a Entrada y...
goto BeginTec ;...continuamos
Poner0
bcf INDF,CDT ;... Si el Dato es un 0, Convertimos el puerto en Salida...
bcf PORTB,CDT ;... ponemos un 0 y continuamos.
BeginTec
movlw 0xFF ;Hecho esto, verificamos si estamos a mitad...
andwf BITTECLADO,W ;... de una previa lectura de tecaldo
btfss STATUS,Z ;... de ser así...
goto LeerTec ;... continuamos con la lectura a medio camino.
bcf TSTATUS,T_PAR ;Si es una nueva lectura, limpiamos el Flag de Paridad...
clrf TPARIDAD ;... la variable intermedia de control de paridad....
clrf TDATA ;... limpiamos la variable que contendrá el dato...
bcf TSTATUS,T_WAIT ;... y el FLAG de espera de subida de reloj
movlw 0x0B ;Debemos leer 11 Bits desde el teclado...
movwf BITTECLADO ;...Usaremos esta variable como contador para ello.
LeerTec
btfss PORTB,TCK ;Testeamos El clock del teclado, si esta en bajo...
goto LeerTec2 ;...indicaría algo para leer, de lo contrario...
bsf PORTB,CCK ;Pasamos el Valor Alto del Clock de Teclado al Computador...
bsf INDF,CCK ;... y convertimos ese puerto a entrada
bsf TSTATUS,T_CKHI ;Este Flag Nos avisará si el Computador desea enviar Algo
bcf TSTATUS,T_WAIT ;...Aun no hay nada para recibir, Limpiamos la espera de subida de reloj...
return ;...y volvemos al bucle principal
LeerTec2
bcf INDF,CCK ;Convertimos el puerto en Salida...
bcf PORTB,CCK ;... y Transmitimos la bajada de Clock al computador
bcf TSTATUS,T_CKHI ;Este Flag Nos avisará si el Computador desea enviar Algo
btfsc TSTATUS,T_WAIT ;...Debemos verificar que el clock haya subido para no leer..
return ;... dos o mas veces el mismo dato
bsf TSTATUS,T_WAIT ;...Seteamos el FLAG de espera de subida de reloj
call ExtraeDato ;Llamamos a una rutina que nos Obtiene el Dato que nos Interesa
decfsz BITTECLADO,F ;Decrementamos el Bit a Leer...
return ;... si aun no terminamos seguimos la rutina de lectura
Tec_CKBajo
btfss PORTB,TCK ;Es el último bit...
goto Tec_CKBajo ;... esperamos que el clock vuelva a nivel alto...
movfw TDATA
movwf TDATA1
btfss TPARIDAD,0 ;Verificamos si hay algun error de Paridad...
return ;... si no hay error regresamos al bucle principal...
; call Reenvio ;...Pero si hay error, pedimos un "Resend"...
return ;...Y volvemos al bucle principal
;-----------------------------  RUTINA PARA EXTRAER LOS DATOS  -----------------------------------
;*** La complejidad de esta rutina consiste en lo siguiente:
;*** Primero distinguir cual de los 11 bits estamos tratando de extraer
;*** Si es el primero, no tiene mayor relevancia, auque debería ser un "0"
;*** Desde el segundo hasta el noveno, son los datos y debemos almacenarlos en "TDATA"
;*** Asimismo debemos verificar la paridad para saber si hubo un error en la transmisión...
;*** ... e iremos almacenando nuestro propio cálculo de paridad en la variable "TPARIDAD"
;*** En el Bit 10 recibiremos el bit de paridad, el cual compararemos con el que nosotros...
;*** hemos calculado, si ocurrió un error dejaremos constancia en la variable "TPARIDAD".
;*** Para finalizar, recibiremos el Bit 11, el bit de Stop, que debiera ser "1"
ExtraeDato
movlw 0x0B ;Buscamos saber si este es el primer bit
xorwf BITTECLADO,W ;Un XOR de BITTECLADO con 0x0B nos dirá si este es el primer Bit
btfsc STATUS,Z ;Si despues del XOR el bit Z del STATUS quedó en "0"
return ;Es el 1º bit, y regreso sin hacer nada
movlw 0x02 ;Probamos ahora si es el bit Nº 10, el de paridad
xorwf BITTECLADO,W ;Un XOR de BITTECLADO con 0x02 nos dirá si este es el Bit de Paridad
btfsc STATUS,Z ;Si despues del XOR el bit Z del STATUS quedó en "0"
goto VerifParidad ;Es el 10º bit, vamos a chequear si la paridad coincide
movlw 0x01 ;Intentemos finalmente con el bit de Stop
xorwf BITTECLADO,W ;Un XOR de BITTECLADO con 0x01 nos dirá si este es el Bit de Stop
btfsc STATUS,Z ;Si despues del XOR el bit Z del STATUS quedó en "0"
return ;Es el 11º bit, y regreso sin hacer nada
;*** Si todo lo anterior falló, es un bit de datos común y silvestre, y vamos a extraer su información
;*** Utilizaremos el acarreo para "empujar" el bit de dato en la variable TDATA y formar el byte completo
bcf STATUS,C ;Asumimos que el Dato es Cero limpiando el bit de acarreo...
btfsc PORTB,TDT ;Verificamos si el dato enviado es un cero...
bsf STATUS,C ;...si no lo es ponemos un 1 en el acarreo
btfsc STATUS,C ;Antes de cargar el valor verificamos si modifica el bit de paridad...
comf TPARIDAD,F ;...si el dato es un "1" el bit de paridad se ve afectado.
rrf TDATA,F ;Finalmente el cero o uno es ingresado al dato
;*** algo interesante, es que el bit es ingresado como MSB, pero en los sucesivos ingresos de datos
;*** se irá desplazando hasta convertirse en LSB
return ;Volvemos a la rutina que nos llamó
VerifParidad
movlw b'00001000' ;Vamos a limpiar el bit de paridad...
andwf PORTB,W ;... que nos envía el teclado...
movwf TGEN ;... y lo almacenaremos en esta variable de propósito General.
movlw b'00001000' ;Hacemos lo propio con el bit de Paridad....
andwf TPARIDAD,W ;... calculado por nosotros
xorwf TGEN,W ;Comparamos ahora los dos bits
bsf TSTATUS,T_PAR ;Asumimos que la paridad esta con error...
btfss STATUS,Z ;...Ahora verificamos si hay diferencia o no...
bcf TSTATUS,T_PAR ;...Si no hay diferencia limpiamos el error de paridad...
return ;...Y regresamos a la rutina que nos llamó.
;*** Cuando el Computador intente enviar datos al teclado, pondrá el clock a estado lógico bajo
;*** El problema es no "pillarme la cola" y pensar que el host está poniendo el clock a "0"  cuando...
;*** Soy yo mismo el que lo está haciendo, esto lo evitamos con un FLAG que me dirá si yo tengo el Clock abajo
LeerCompu
btfss TSTATUS,T_CKHI ;Chequeo el FLAG de Clock
return ;Yo lo puse a nivel bajo, no obtengo nada con chequearlo ahora.
btfsc PORTB,CCK ;Sabiendo que puse un 1 en el Clock, verifico si se encuentra en 0
return ;Esta en uno, como yo lo dejé, el host no intenta enviar ningún dato.
;*** Hay algo en el horizonte, parece que el host intenta decirle algo al teclado
;*** Lo primero que tenemos que saber es que a nuestro CheatBox no le interesa nada de lo que el host
;*** Tenga para decir, nos limitamos a transmitir la información que nos llega del host al teclado
;*** Otro dato a tener en cuenta es si el host esta enviando datos o solo inhibiendo al teclado
;*** Esto es importante para saber si debo abortar o no la transmisión en curso.
;*** Una simple inhibición, dejaría la linea de Datos en alto y liberaría el clock
;*** Una condición RTS, implicaría poner a nivel bajo la línea de Datos y despues liberar el clock
bcf INDF,TCK ;Convertimos el puerto clock del teclado en Salida...
bcf PORTB,TCK ;... y Transmitimos la bajada de Clock al Teclado
Inhib_RTS
btfss PORTB,CDT ;Probamos la linea de Datos...
goto Req_to_Send ;... lo temido... El computador intenta enviar datos
btfss PORTB,CCK ;... Sigue a nivel alto, aun no pasa nada, pobamos el Clock
goto Inhib_RTS ;... Continua a Nivel bajo, Volvemos a probar
bsf PORTB,TCK ;Era solo una inhibición temporal, Transmitimos la subida de Clock al Teclado...
bsf INDF,TCK ;...y convertimos el puerto clock del Entrada
return ;Volvemos al bucle principal, aquí no ha pasado nada.
;*** Esta rutina se encargará de leer el Data del host y enviarlo al Teclado, y leer el Clock...
;*** ...del Teclado y enviarselo al host, hasta que se detecte el fin de la transmisión.
;*** La forma que voy a implementar para detectar el fin de la transmisión del host, es descubrir el bit
;*** de Stop del host (que es un "1" lógico) y prestar atención a cuando el teclado lo convierta en "0"
;*** Indicando un ACK, y por lo tanto el fin de la transmisión.
Req_to_Send
bcf INDF,TDT ;Convertimos el puerto de datos del teclado en Salida...
bcf PORTB,TDT ;... y Transmitimos el nivel lógico bajo al teclado
Libera_CK
btfss PORTB,CCK ;Ahora debo esperar a que el host...
goto Libera_CK ;...libere el clock
bsf PORTB,TCK ;Transmito esto al Teclado, Poniendo un uno en el Clock del Teclado...
bsf INDF,TCK ;... Y transformándolo en entrada.
RTS_Loop
btfss PORTB,TCK ;Testeo el Nivel Logico del Clock del Teclado...
goto RTS_CCK_0 ;En este caso un cero... pongamoslo en el Clock del Host
bsf PORTB,CCK ;En este caso un uno, ponemos un uno y....
bsf INDF,CCK ;... convertimos el puerto en entrada.
goto RTS_Test_CDT ;Vemos que hay en la linea de datos del host
RTS_CCK_0
bcf INDF,CCK ;En este caso un cero, Convertimos el puerto en salida...
bcf PORTB,CCK ;...y ponemos un valor lógico de cero en él
RTS_Test_CDT
btfss PORTB,CDT ;Testeo el Nivel Logico de la linea de datos del host...
goto RST_TDT_0 ;En este caso un cero... pongamoslo en la linea de datos del teclado
bsf PORTB,TDT ;En este caso un uno, ponemos un uno y...
bsf INDF,TDT ;... convertimos el puerto en entrada.
bsf CSTATUS,C_DTHI ;Además marcamos el Flag correspondiente
goto RTS_Test_Stop ;Averiguemos si llegamos al bit final...
RST_TDT_0
bcf INDF,TDT ;En este caso un cero, Convertimos el puerto en salida...
bcf PORTB,TDT ;...y ponemos un valor lógico de cero en él
bcf CSTATUS,C_DTHI ;Además marcamos el Flag correspondiente
RTS_Test_Stop
btfss CSTATUS,C_DTHI ;Determinamos si el FLAG dice que puse un 1 en la salida
goto RTS_Loop ;No, hay un cero, Seguimos con la transmisión.
btfsc PORTB,TDT ;Si, hay un uno, vemos si el tecladolo cambió por un cero
goto RTS_Loop ;No hay cambio, continuamos con la transmisión
;*** Fin de la transmisión
clrf BITTECLADO ;Limpiamos el Contador de bits de teclado para empezar de cero
RST_End
btfss PORTB,TDT ;Esperamos a que el bit de datos del teclado suba a 1
goto RST_End ;Antes de regresar a la rutina principal
return ;Volvemos a la rutina que nos llamó
end ;Fin del Programa