Emulador En Puerto Paralelo

Transcripción

Emulador En Puerto Paralelo
TRABAJO – EMULADOR DE
INSTRUCCIONES PARA EL PIC,
CON COMUNICACIÓN POR
PUERTO PARALELO
MIEMBROS DEL GRUPO:
Fernández Perdomo, Enrique
Mora Madrid, Gastón Martín
Ventura García, Yeray
INFORMACIÓN ACADÉMICA:
Prácticas de Diseño de Sistemas Basados en Microprocesador
3º Ingeniería en Informática – 2º Cuatrimestre
Índice
1. Objetivos .......................................................................................................................................................3
1.1. Protocolo del Puerto Paralelo ...................................................................................................................3
1.2. Intérprete Gráfico del PC (Emulador) ........................................................................................................4
1.3. Ejecución de Instrucciones Emuladas en el PIC ..........................................................................................4
2. Fundamento Teórico .......................................................................................................................................5
2.1. Descripción General ................................................................................................................................5
2.1.1. Acceso Directo al Puerto ...................................................................................................................5
2.1.2. Registro de Datos (D) .......................................................................................................................5
2.1.3. Registro de Estado (S).......................................................................................................................6
2.1.4. Registro de Control (C) .....................................................................................................................6
2.1.5. Bit de Puerto Bidireccional.................................................................................................................6
2.1.6. Bit de Interrupción ............................................................................................................................7
2.1.7. Interrupciones con el Puerto Paralelo .................................................................................................7
2.2. Programación..........................................................................................................................................7
2.2.1. Obtención del Puerto........................................................................................................................7
2.2.2. Acceso a los Puertos.........................................................................................................................8
2.2.3. Detección del Tipo de Puerto ............................................................................................................9
2.3. Descripción del Conector Físico..............................................................................................................10
2.3.1. Velocidad ......................................................................................................................................11
2.3.2. Características Técnicas ..................................................................................................................11
3. Desarrollo ....................................................................................................................................................13
3.1. Protocolo del Puerto Paralelo .................................................................................................................13
3.1.1. Definición y Explicación del Protocolo ..............................................................................................13
3.1.2. Envío desde el PC ..........................................................................................................................13
3.1.3. Recepción desde el PC ...................................................................................................................15
3.1.4. Envío desde el PIC..........................................................................................................................17
3.1.5. Recepción desde el PIC ..................................................................................................................19
3.1.6. Inicialización y Acceso al Puerto Paralelo desde el PC.......................................................................20
3.1.7. Inicialización del PIC.......................................................................................................................23
3.2. Intérprete Gráfico del PC (Emulador.exe) ................................................................................................24
3.2.1. Traducción de Lenguaje Natural......................................................................................................24
3.2.1.1. Fase 1: Traducción de Instrucciones .........................................................................................24
3.2.1.2. Fase 2: Compilación de Instrucciones.......................................................................................28
3.2.2. Manejo del Programa Gráfico.........................................................................................................30
3.2.2.1. Creación de Programas emulados............................................................................................32
3.3. Programa de Emulación de Instrucciones del PIC.....................................................................................34
3.4. Rutinas de Retardo para el PIC ...............................................................................................................37
3.5. Fichero Cabecera para el PIC ................................................................................................................37
3.6. Directivas especiales de Ensamblador MASM32 ......................................................................................38
3.7. Interrelación entre Programas .................................................................................................................44
3.7.1. Programa del PC............................................................................................................................44
3.7.2. Programa del PIC ...........................................................................................................................44
3.8. Ejecución emulada en Proteus® de la Placa PIC-Trainer ..........................................................................45
4. Conclusiones................................................................................................................................................48
4.1. Protocolo del Puerto Paralelo .................................................................................................................48
4.2. Intérprete Gráfico del PC (Emulador) ......................................................................................................49
4.3. Ejecución de Instrucciones Emuladas en el PIC ........................................................................................49
5. Bibliografía...................................................................................................................................................50
6. Anexo ..........................................................................................................................................................51
6.1. Códigos del PIC ....................................................................................................................................51
6.1.1. Emulador de Comandos (main.asm)................................................................................................51
6.1.2. Protocolo del Puerto Paralelo (paralelo.asm) ....................................................................................53
6.1.3. Rutinas de Retardo (retardo.asm).....................................................................................................55
6.1.4. Fichero Cabecera (InstrPIC16F84.h)................................................................................................57
6.2. Códigos del PC .....................................................................................................................................59
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
1
6.2.1. Interfaz del Programa Gráfico Principal (Emulador.inc) .....................................................................59
6.2.2. Programa Gráfico Principal (Emulador.asm).....................................................................................61
6.2.3. Interfaz del Intérprete de Comandos (TraduceIns.inc)........................................................................72
6.2.4. Intérprete de Comandos (TraduceIns.asm) .......................................................................................73
6.2.5. Fichero de Reglas de Sintaxis (reglasComp.txt) .................................................................................76
6.2.6. Interfaz del Compilador de Comandos (Compilador.inc)...................................................................77
6.2.7. Compilador de Comandos (Compilador.asm) ..................................................................................77
6.2.8. Interfaz del Acceso al Puerto Paralelo (Paralelo.inc) ..........................................................................82
6.2.9. Acceso al Puerto Paralelo (Paralelo.asm) .........................................................................................83
6.2.10. Interfaz del Protocolo del Puerto Paralelo (Protocolo.inc).................................................................84
6.2.11. Protocolo del Puerto Paralelo (Protocolo.asm) ................................................................................84
6.2.12. Interfaz de la Librería Winio (WinIo.inc) .........................................................................................86
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
2
1. Objetivos
En este trabajo se ha realizado un emulador de instrucciones, el cual consistirá en un programa
gráfico para Win32, que permitirá interpretar instrucciones y convertirlas a un pseudocódigo que
posteriormente se tratarán con valores hexadecimales de un byte. Con esto se consigue poder enviar
cada comando y sus argumentos al microcontrolador PIC16F84. En el PIC se tendrá todos los comandos
reales, que se emularán desde el programa del PC. De este modo, finalmente se ejecutarán en el PIC los
comandos emulados, que serían como pseudoinstrucciones de un más alto nivel.
Por otro lado, será necesaria la implementación de un protocolo de comunicación entre el PC y
el PIC (y viceversa, es decir, que tendrá que ser bidireccional). Esto protocolo se desarrollará en el marco
del puerto paralelo, con una transmisión de un byte en cada transmisión (8 bits), usando dos señales de
control o sincronismo, a parte de las del dato.
En los siguientes apartados se comenta de forma algo más detallada aquéllos aspectos
desarrollados en el ámbito de cada una de las tareas antes mencionadas.
1.1. Protocolo del Puerto Paralelo
En cuanto al protocolo del puerto paralelo, se tratará su implementación software, tanto
en el PC como en el PIC. Hay que destacar el hecho de que serán implementaciones similares
aunque en lenguajes ensamblador ligeramente distintos (ensamblador del PIC, frente al
ensamblador gráfico MASM32). Pero además de esto nos enfrentaremos con el hecho de que
algunas líneas de control usarán lógica negada, aspecto que habrá que tratar con especial
cuidado.
De este modo, para el diseño de la interfaz de conexión y comunicación por puerto
paralelo, el primer paso será el diseño hardware con un cable paralelo conectado desde la
ranura del PC al zócalo libre de la placa PIC-Trainer, para poder recibir y enviar datos entre PC y
PIC.
Una vez realizada la construcción hardware se creará el diseño conceptual del protocolo
y su posterior implementación, tanto en el PIC como en el PC. Este protocolo constará de las
típicas instrucciones de envío y recepción, que se usarán en PC y PIC, y como es obvio, cuando el
PC envíe el PIC recibirá, y viceversa.
Igualmente, en la fase de implementación software se diseñará una etapa previa de
inicialización del protocolo, que atañerá a los puertos del PIC, y estado del puerto paralelo del
PC. Así, se definirá lo que puede entenderse como un estado de reposo. Mientras en el PIC el
proceso de inicialización atañerá exclusivamente al modo de los puertos (entrada o salida) y su
contenido, en el caso del PC también se tratará el proceso de toma de las direcciones en que
está mapeado el puerto paralelo.
Finalmente, hay que mencionar que al desarrollar el programa del PC en modo gráfico
estaremos arrancando nuestro programa en modo usuario, en lo que se conoce como el modo
protegido de funcionamiento típico del entorno gráfico de Windows. En este modo, no se podrá
tener acceso a toda la memoria ni a los puertos del PC. Esto se debe a que es el sistema
operativo (en este caso Windows), el que lo controla. Para solventar este problema se usará la
librería WinIo, que se comentará en la memoria, y que permite el uso de la memoria a través de
una función API específica que esta librería aporta y, lo que es más interesante, las operaciones
de entrada y salida (in y out, respectivamente) sobre los puertos del sistema (el PC) serán
permitidas. Con esta librería podrá realizarse todo este proceso incluso en máquinas con
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
3
Windows de redes (NT y XP), en los que la protección del equipo es aún mayor. Todo esto será
posible siempre y cuando en la BIOS del PC se haya configurado con anterioridad el modo
Bidireccional.
1.2. Intérprete Gráfico del PC (Emulador)
El intérprete gráfico tendrá la finalidad de poder cargar programas en un lenguaje
emulado, que permite el uso de comandos con parámetros de entrada y salida, que emulan
instrucciones que realizan una determinada tarea en el PIC.
De este modo, se tendrá una típica interfaz de programa gráfico, desarrollada en
ensamblador MASM32, en la que se puede editar el código de nuestra aplicación, así como
compilar y ver resultados. Además, nuestros programas podrán guardarse fácilmente, sin
problema alguno. Además de las opciones de compilación, se tendrán opciones de tratamiento
especial y testeo del puerto, con finalidades de depuración, fundamentalmente.
Para que el conjunto de instrucciones o comandos posible, así como su conjunto de
parámetros de entrada y salida, quede bien definido, se hará uso de un fichero especial. Este
será un fichero típico de sintaxis en el que se definirán las reglas de estos comandos y sus
mencionados parámetros de entrada y salida.
Finalmente, sólo hay que indicar el hecho de que el proceso de compilación de nuestros
programas conllevará la comunicación con el PIC a través del puerto paralelo, con el protocolo
desarrollado, para que el comando deseado se ejecute en el PIC, con los parámetros de entrada
indicados, e igualmente se devuelvan los resultados en los parámetros de salida indicados. Para
facilitar el manejo del programa, el lenguaje emulado creado incluirá la posibilidad de definición
de variables en la cabecera de nuestro programa, para su posterior uso entre comandos.
1.3. Ejecución de Instrucciones Emuladas en el PIC
En el PIC16F84 se cargará un programa con todas las librerías de manejo de la placa
PIC-Trainer y PIC-Trainer Plus. El programa principal esperará el envío de un comando. Una vez
recibido éste, se bifurcará a la zona apropiada para su ejecución en el PIC. Con esto se tendrá
cerrado el bucle de emulación.
Si los comandos a ejecutar requieren el uso de parámetros de entrada, el programa se
dispondrá a recibirlos, mientras que desde el PC se enviarán. Con ello, los comandos ejecutados
tendrán mayor versatilidad.
Finalmente, el programa tendrá las rutinas necesarias para el correcto envío y recepción
de comandos y parámetros, para lo cual se usa el protocolo diseñado para el puerto paralelo.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
4
2. Fundamento Teórico
A continuación se comentan por separado los aspecto programáticos y físicos del Puerto Paralelo,
desde su interfaz, como conexionado físico.
2.1. Descripción General
El puerto paralelo se identifica por su dirección de I/O base y se identifica ante sistemas
DOS por el número LPT. Cuando arranca la máquina, la BIOS chequea direcciones específicas
de I/O en busca de puertos paralelos y construye una tabla de las direcciones halladas en la
posición de memoria 40h:8h (o 0h:0408h). Esta tabla contiene hasta tres palabras de 16 bits.
Cada palabra es la dirección de I/O base del puerto paralelo. La primera palabra corresponde a
LPT1, la segunda a LPT2 y la tercera a LPT3. Hay que agregar que en DOS tenemos el dispositivo
PRN que es un alias a uno de los dispositivos LPT (generalmente es LPT1, pero se puede cambiar
con la orden MODE) Las direcciones estándar para los puertos paralelos son 03BCh,0378h y
0278h (chequeadas en este orden).
Desde el punto de vista del software, el puerto paralelo son tres registros de 8 bits cada
uno, ocupando tres direcciones de I/O consecutivas de la arquitectura x86.
2.1.1. Acceso Directo al Puerto
El puerto, como se mencionó antes, consiste de tres registros de 8 bits ubicados
en direcciones adyacentes del espacio de I/O de la PC. Los registros se definen relativos a
la dirección de I/O base (variable IOBase) y son:
IOBase+0 : registro de datos
IOBase+1 : registro de estado
IOBase+2 : registro de control
2.1.2. Registro de Datos (D)
Se puede leer y escribir. La forma de leer y escribir puertos con lenguajes de
programación estándares se puede ver en la sección Acceso a los puertos. Escribiendo un
dato al registro, causa que el mismo aparezca en los pines 2 a 9 del conector del puerto.
Leyendo el registro, se lee el último dato escrito (NO lee el estado de los pines; para ello
hay que usar un puerto bidireccional).
Nro.Bit 7
x
.
.
.
.
.
.
.
6
.
x
.
.
.
.
.
.
5
.
.
x
.
.
.
.
.
4
.
.
.
x
.
.
.
.
3
.
.
.
.
x
.
.
.
2
.
.
.
.
.
x
.
.
1
.
.
.
.
.
.
x
.
0
.
.
.
.
.
.
.
x
Descripción
D7 (pin 9), 1=Alto, 0=Bajo
D6 (pin 8), 1=Alto, 0=Bajo
D5 (pin 7), 1=Alto, 0=Bajo
D4 (pin 6), 1=Alto, 0=Bajo
D3 (pin 5), 1=Alto, 0=Bajo
D2 (pin 4), 1=Alto, 0=Bajo
D1 (pin 3), 1=Alto, 0=Bajo
D0 (pin 2), 1=Alto, 0=Bajo
Cuando se indica Alto o Bajo se refiere a la tensión de salida (~5V para el 1
físico y ~0V para el 0 físico, respectivamente). Esto es porque la lógica puede ser positiva
(un 1 lógico equivale a Alto o 5V) o negada (un 0 lógico equivale a Bajo o 0V). Con
respecto a esto debemos decir que para negar algo le anteponemos el carácter /
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
5
(representando la barra que se coloca encima). El estándar es que las salidas sean LS TTL
(low schottky TTL), aunque las hay que son de tipo OC (colector abierto). La corriente que
pueden entregar (source) es de 2,6 mA máximo y pueden absorber (sink) un máximo de
24 mA. En el puerto original de IBM hay condensadores de 2,2 nF a masa. Las tensiones
para el nivel bajo son entre 0 y 0,8V y el nivel alto entre 2,4V y 5V.
2.1.3. Registro de Estado (S)
El registro de estado está en IOBase+1. Es de sólo lectura (las escrituras serán
ignoradas). La lectura da el estado de los cinco pines de entrada al momento de la
lectura. En la tabla siguiente los nombres de los pines se dejaron en inglés porque es
como generalmente se identifican.
Nro.Bit 7
x
.
.
.
.
.
6
.
x
.
.
.
.
5
.
.
x
.
.
.
4
.
.
.
x
.
.
3
.
.
.
.
x
.
2
.
.
.
.
.
x
1
.
.
.
.
.
x
0
.
.
.
.
.
x
Descripción
S7 : Busy (pin 11), 0=Alto, 1=Bajo
S6 : Ack (pin 10), 1=Alto, 0=Bajo
S5 : No paper (pin 12), 1=Alto, 0=Bajo
S4 : Selected (pin 13), 1=Alto, 0=Bajo
S3 : Error (pin 15), 1=Alto, 0=Bajo
Sin definir
La línea Busy tiene, generalmente, una resistencia de pull-up interna. El estándar
es que sean entradas tipo LS TTL.
2.1.4. Registro de Control (C)
El registro de control se encuentra en IOBase+2. Es de lectura/escritura.
Nro.Bit 7
x
.
.
.
.
.
.
6
x
.
.
.
.
.
.
5
.
x
.
.
.
.
.
4
.
.
x
.
.
.
.
3
.
.
.
x
.
.
.
2
.
.
.
.
x
.
.
1
.
.
.
.
.
x
.
0
.
.
.
.
.
.
x
Sin usar
C5 : Control puerto bidireccional
C4 : Interrupt control, 1=enable, 0=disable
C3 : Select (pin 17), 1=Bajo, 0=Alto
C2 : Initialize (pin 16), 1=Alto, 0=Bajo
C1 : Auto Feed (pin 14), 1=Bajo, 0=Alto
C0 : Strobe (pin 01), 1=Bajo, 0=Alto
Los cuatro bits inferiores son salidas. La lectura devuelve lo último que se escribió
a dichos bits. Son TTL a colector abierto con resistencias de pull-up de 4700 ohms, por lo
que un dispositivo externo puede forzar el estado de los pines sin dañar el driver. Esto
permite utilizar estas cuatro líneas como entradas. Para ello, ponemos en alto las cuatro
salidas (escribiendo 0000100b en IOBase+2) lo que hace que las salidas “floten”.
Ahora, un dispositivo externo puede forzar a bajo alguna de las salidas con lo que,
leyendo el puerto, sabemos si esto sucedió o no. Es posible realizar esta técnica en
salidas totem-pole (como D0-D7) pero no recomendamos su uso porque habría que tener
un conocimiento preciso de la corriente ya que se puede sobrecargar los transistores de
salida, dañando el driver (especialmente en puertos integrados LSI).
2.1.5. Bit de Puerto Bidireccional
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
6
El bit C5, está disponible sólo si se trata de un puerto bidireccional; en los puertos
comunes actúa como los bits C6 y C7. Si C5=1, el buffer de los datos de salida se pone
en alta impedancia, “desconectando” dicho buffer de los pines 2 a 9 del conector del
puerto (D0 a D7). Si se escribe al registro de datos, se escribe al buffer pero no a la
salida. Esto permite que al leer el puerto, se lea el estado de las entradas y no lo que hay
en buffer. En las computadoras IBM PS/2, para habilitar el puerto paralelo bidireccional,
además de lo antes descrito, se debe poner a 1 el bit 7 del registro del puerto 102h
(opciones de configuración). En computadoras que no tengan puerto paralelo
bidireccional compatible PS/2 hay que modificar uno o más bits de algún puerto
específico correspondiente al chipset de la placa. A veces se habilita por setup o por
jumper en la placa del puerto.
2.1.6. Bit de Interrupción
En trabajos normales de impresión ni el BIOS ni el DOS hacen uso de la
interrupción. El hecho de poseer una línea de interrupción que está conectada
directamente al PIC (Programmable Interrupt Controller), lo hace muy útil para
experimentación en data-loggers por ejemplo. El bit de interrupción está conectado al
control de un buffer de tres estados. Cuando C4=1, se activa el buffer y su entrada, S6,
se conecta a la línea IRQ (en general es IRQ7 o IRQ5). La lectura del bit, nos devuelve el
estado del mismo (es decir si el buffer está en alta impedancia o no). Se producirá una
interrupción, cuando haya un flanco descendente en el pin correspondiente a S6 (ver
Descripción del conector físico). A continuación, se describen los pasos para poder utilizar
interrupciones.
2.1.7. Interrupciones con el Puerto Paralelo
Primero que nada debemos habilitar el buffer que conecta la línea ACK con la
línea IRQ. Esto lo hacemos poniendo a 1 el bit 4 del registro de control (IOBase+2).
Luego debemos preparar una ISR (Interrupt Service Routine) que atienda la interrupción
recordando enviar la señal EOI (20h) al registro de control del PIC (puerto 20h) al salir de
la rutina. La interrupción software corresponde al número 0Dh para IRQ5 y 0Fh para
IRQ7. Finalmente se habilita con 0 la interrupción IRQ5 (o IRQ7) escribiendo al bit 5 (o
7) del registro de interrupciones del PIC (puerto 21h). Para desinstalar la ISR,
deshabilitamos la IRQ5 (o IRQ7) escribiendo un 1 al bit (o 7) del registro de
interrupciones del PIC (puerto 21h). Luego hacemos que C4=0.
2.2. Programación
Por programación entenderemos la programación necesaria para el puerto paralelo,
desde el PC, por lo general en máquinas Pentium y 80x86, con Windows como sistema
operativo.
2.2.1. Obtención del Puerto
Como ya se mencionó anteriormente las direcciones de I/O de los puertos
paralelo se almacenan en una tabla ubicada en 40h:8h (0h:408h). Entonces, éste sería
un método de obtener las direcciones. A continuación se muestra como obtener dichas
direcciones en distintos lenguajes.
Ensamblador
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
7
;en SI tengo la dirección de memoria:
;LPT1 = 0408h
;LPT2 = 0408h + 2h = 040Ah
;LPT3 = 040Ah + 2h = 040Ch
mov si,0408h
;SI = 0408h
xor ax,ax
;AX = 0
push ds
;Mete DS en la pila
mov ds,ax
;DS = AX = 0
mov ax,word ptr [SI] ;AX = [0h:SI]
pop ds
;recupero DS de la pila
;ahora en AX tengo la dirección base
Windows
En entorno Windows, se complica un poco ya que tenemos varios métodos.
1- Verificar en las direcciones estándar que el puerto tenga retención de datos escritos en
él. Es armar la tabla que realiza la BIOS por nosotros mismos. Este método falla si el
puerto es bidireccional (o ECP o EPP), si algún controlador prohíbe el acceso, si estamos
bajo WinNT o si el puerto está en una dirección no estándar. Ver la sección Acceso a los
puertos para más detalles y véase la sección Detección de tipo de puerto, para buscar por
uno mismo las direcciones (y de paso detectar que tipo de puerto es). Ahora lo único que
podría hacer fallar la prueba es si algún controlador de dispositivo prohíbe el acceso (o
WinNT, claro).
2- Tener la posibilidad de leer la tabla que la BIOS genera cuando arranca la máquina.
Debemos contar con alguna función que nos permita leer una dirección de memoria
como si estuviéramos en modo real de la CPU.
3- Bajo WinNT, podemos obtener (mediante las funciones de la API, RegOpenKey y
RegQueryValue) la información del registro de:
[HKEY_LOCAL_MACHINE\Enum\Root\
[HKEY_LOCAL_MACHINE\Enum\BIOS\
[HKEY_DYN_DATA\Config Manager\Enum\
4- En Win9x,Me, podemos hacer el enfoque de la opción 2, pero con la API de Windows.
Utilizamos la función de la API Toolhelp32ReadProcessMemory (que reside en Kernel32 y
que no se encuentra en NT).
2.2.2. Acceso a los Puertos
A continuación se darán las funciones a utilizar para leer y escribir puertos en
distintos lenguajes. En Ms-Dos no tenemos ningún tipo de restricción de acceder a los
puertos. En Windows 3.x, 9x y Me tampoco hay restricciones (a no ser que el puerto esté
bajo el control de un controlador de dispositivo virtual). En Windows NT, el sistema
operativo tiene control total sobre la máquina por lo que hay que pedir un permiso que se
hace mediante un driver.
Ensamblador
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
8
Los opcodes (códigos de operación) IN y OUT permiten leer y escribir,
respectivamente, datos a un puerto cualquiera.
La secuencia a seguir para leer podría ser:
mov dx,Puerto ;DX = Puerto (puede ser cte. o ref. de memoria si es variable).
in al,dx
;Leo del puerto DX y lo guardo en AL
Y para escribir:
mov dx,Puerto ;DX = Puerto (puede ser cte. o ref. de memoria si es variable).
out dx,al
;Manda AL al puerto DX
2.2.3. Detección del Tipo de Puerto
En esta sección indicaremos los pasos a seguir para detectar si el puerto es SPP
(común), bidireccional compatible PS/2, ECP o EPP. Describiremos los pasos generales;
luego se tendrá que adaptar al lenguaje ensamblador. Se testea en orden descedente de
complejidad, es decir primero ECP, luego EPP, bidireccional y finalmente SPP (esto se
debe realizar así para no fallar en la detección).
Detectando ECP
Este es el método que recomienda Microsoft:
Leer el control de registro extendido (ECR) en Base+402h. Base se refiere a la
dirección inicial donde se comienza a mapear el puerto (03BCh,0378h y 0278h).
Verificar que el bit 0 sea 1 (FIFO vacía) y que el bit 1 sea 0 (FIFO no está llena). Estos bits
podrían ser distintos que los bits correspondientes en el puerto de control (Base+2h). Para
verificar esto, cambiamos el estado de algún bit del puerto Base+2h y verificamos que no
haya cambiado en Base+402h. Una prueba adicional es escribir 34h al ECR y leerlo. Los
bits 0 1 y son de sólo lectura, por lo tanto, si leemos 35h, es probable que tengamos un
puerto ECP.
Detectando EPP
Además de los tres registros de un puerto SPP, un puerto EPP tiene cinco registros
más mapeados desde Base+3h a Base+7h. Estos registros adicionales proveen una
manera de testear la presencia de un EPP, escribiendo ciertos valores y leyendo el
resultado (de la misma manera que se testea un puerto SPP). Al igual que al detectar
puertos SPP, se recomienda escribir un par de valores y leerlos. Dichos valores podrían ser
55h y AAh. Hay que asegurarse de poner S0 a 0 (EPP timeout), antes de leer o escribir a
estas direcciones extendidas. Otro dato importante es que no existen puertos EPP en la
dirección base 03BCh.
Detectando SPP
Para detectar si es SPP, hacemos el procedimiento que realiza la BIOS al
arranque. Verificamos la retención del puerto, que será posible en un puerto SPP. Para
ello escribimos un par de valores (55h y AAh) a las direcciones base (03BCh,0378h y
0278h) y leemos el registro verificando que sean los mismo valores escritos.
Detectando puerto bidireccional (PS/2)
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
9
Aquí debemos habilitar el puerto bidireccional (con C5=1) y hacer la misma
prueba que para un SPP para verificar que no haya retención.
2.3. Descripción del Conector Físico
El puerto es un conector hembra DB25 (cuyo diagrama y señales utilizadas podemos ver
en la siguiente figura) con doce salidas latcheadas (que tienen memoria/buffer intermedio) y
cinco entradas, con 8 líneas de masa. La función normal es transferir datos a una impresora a
través de las 8 líneas de datos, usando las señales restantes como control de flujo.
Como se mencionó anteriormente, la conexión del puerto paralelo al mundo externo se
realiza por un conector hembra DB25. Viendo el conector al frente y con la parte que tiene más
pines hacia arriba, se numera de menor a mayor de derecha a izquierda en ambas filas de pines
(1 a 13 arriba y 14 a 25 abajo).
En las tablas vistas en las secciones correspondientes a cada registro se vio que cada bit
tiene un nombre característico. Estos nombres son las funciones que cumplen los bits en una
conexión con una impresora. Además de la lógica que posee cada señal (que es un aspecto
físico del conector), tenemos que la impresora tiene su propia lógica para con los nombres de los
pines.
Este es el momento para hacer mención a las líneas que nosotros utilizaremos para
implementar nuestro cacle paralelo. En primer lugar necesitaremos 2 líneas de control para que
el PC y el PIC sean capaces de entenderse sin confusión. Estas serán la línea STROBE y ACK, y
como parece obvio para la transmisión de los datos emplearemos las correspondientes 8 líneas
de datos del puerto. Las 2 líneas de control se conectan al puerto A y como estas señales son tan
importantes deberemos establecer que una de esas líneas es intocable por el usuario, en cambio
la de datos no tienes esa relevancia por que aunque se escriba en ellas el PC y el PIC seguirán
comunicándose y determinando si todo el proceso ha sido correcto.
Por eso no hay que confundirse si el nombre de la señal establece una lógica contraria a
la lógica real del puerto; son cosas distintas. Por ejemplo, la expresión /Ack, donde Ack viene de
Reconocido o Aceptado, nos indica que una aceptación (Ack=Alto=Verdadero) la tenemos que
reconocer porque la impresora nos envía un Bajo, que por la característica de S6 equivale a un 0
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
10
lógico. En la columna de entrada/salida (I/O) se refiere al lado de la PC. En la columna Cent
pin, indicamos el pin correspondiente al conector Centronics usado en las impresoras. Algunos
nombres no se corresponden con los de las tablas anteriores. Es a propósito para indicar los
posibles nombres que puede tener una señal.
I/
O
O
DB25
pin
1
Cent
pin
1
Bit
reg.
/C0
Señal
Descripción
/Strobe
O
O
O
O
O
O
O
O
I
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
D0
D1
D2
D3
D4
D5
D6
D7
S6
Data0
Data1
Data2
Data3
Data4
Data5
Data6
Data7
/Ack
A bajo por más de 0.5uS para
indicar a la impresora que se
enviarán datos
Bit menos significativo de Data
I
11
11
/S7
Busy
I
I
O
12
13
14
12
13
14
S5
S4
/C1
PaperEnd
SelectIn
/AutoFd
I
15
32
S3
/Error
Masa
Bit más significativo de Data
Pulso bajo de ~5uS, indica que se
recibieron datos en impresora.
En alto nos indica que la impresora
Está ocupada
En alto nos indica que no hay papel
En alto para impresora seleccionada
Si ponemos en bajo, el papel se
mueve una línea luego de la
impresión
En bajo nos indica error (no hay
papel, está fuera de línea, error
no det.)
Si enviamos un pulso en bajo > 50uS
La impresora se resetea
En bajo seleccionamos impresora (en
gral. no se usa, ya que SelectIn se
fija a alto)
Masa retorno de par trenzado
Masa
Masa
Masa lógica
Masa chasis
O
16
31
C2
/INIT
O
17
36
C3
/Select
-
18-25 19-30
y 33
18-25 16
18-25 17
2.3.1. Velocidad
Un puerto paralelo ISA normal toma un ciclo-ISA para leer o escribir. Como en
muchos sistemas, la velocidad del bus ronda los 1,3 Mhz, podemos decir que la lectura la
podemos hacer cada 1 uS (idealmente, ya que siempre tenemos otras instrucciones
software, etc, etc.). Algunos puertos soportan un modo “turbo” que elimina los 3 estados
de espera de la CPU, con lo que la velocidad de lectura/escritura al puerto se duplica
(2,7 MHz).
2.3.2. Características Técnicas
Las características técnicas del puerto paralelo son:
Tensión de alimentación (VDD) ............................................... 9 a 12 volts D.C.
Tensión de alimentación alta potencia (VHH)............................ 0 a 15 volts
Tensión de Salida VCC........................................................... 5 Vcc regulados
Capacidad de carga de la salida VCC..................................... 1A
Cantidad de entradas TTL ....................................................... 4
Niveles aceptables para entradas TTL ...................................... 0v a 5v
Cantidad de salidas TTL (TTLx) ................................................ 8
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
11
Capacidad de carga en salidas TTL ......................................... 2 mA
Cantidad de salidas de alta potencia (HPx)............................... 7
Capacidad de carga en salidas de alta potencia....................... 500 mA
Dimensiones .......................................................................... 150mm x 100mm
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
12
3. Desarrollo
Para la elaboración de esta práctica, la idea consiste en la comunicación de un PC y un PIC a
través de un puerto paralelo, el cual esta conectado a un sócalo de la placa del PIC-Trainer, que a su
vez éste se conecta a: los cables correspondientes a los datos enviados o recibidos desde el PC a las 8
líneas del puerto B del PIC, y las dos líneas necesarias para el control de las transferencias (mediante un
pequeño protocolo) conectadas al puerto A. Estas líneas no deberán ser empleadas por el usuario,
puesto que si son tocadas, se perderá el sincronismo entre el PC y el PIC a la hora de la transferencia.
Por último decir que hay una línea más que es la tierra del paralelo, que se conecta a la tierra de
referencia del PIC, a través del mismo sócalo. Todo esto es la idea de lo que haremos, puesto que esto
que se ha dicho viene soportado por unos ficheros donde se ha implementado el protocolo de envío y
recepción de datos (protocolo.asm), así como el programa cargado en el PIC, y el entorno gráfico desde
el que se manejan las transferencias (emulador.asm Æ emulador.exe).
3.1. Protocolo del Puerto Paralelo
En los siguientes apartados se trata el protocolo de comunicación a través del puerto
paralelo. Para ello se empieza por definir y explicar dicho protocolo de forma conceptual.
Seguidamente se trata el envío y recepción, tanto desde el PIC como desde el PC. Del mismo
modo se comentan otros aspectos relacionados, así como la inicialización y estado por defecto o
de reposo de los elementos conectados al puerto, haciendo especial hincapié en las líneas de
control del mismo.
3.1.1. Definición y Explicación del Protocolo
El protocolo no es más que una serie de reglas que se deben cumplir para
sincronizar el receptor y el emisor en una transmisión de información. Es te sincronismo se
consigue gracias a un control a la hora de transmitir y recibir que a continuación
empezaremos a comentar. Un hecho clave es que un dato no puede ser enviado si el
receptor no esta listo para recibir la información debido a que pueda estar realizando
otra tarea, (recibiendo de otro emisor, o tratando la información anteriormente recibida),
si no se quiere perder la información. Una vez que el emisor este listo para su labor,
deberá informar al emisor para que este comience a transmitir. A parte de esto tendremos
unos estados en los que lo que hacemos es esperar un tiempo para evitar la
desincronización. Teniendo estos conceptos claros empezaremos a describir tal proceso
de forma breve y directamente sobre el código en forma de comentarios. Hay que tener
en cuenta el hecho de que las líneas de control asociadas al puerto A del PIC (A3 y A4)
son en lógica inversa.
3.1.2. Envío desde el PC
Todos estos pasos que describiremos en este apartados y en los posteriores están
implementados en el archivo protocolo.asm.
Enviar proc Dato:DWORD
;
Envía Dato de PC al PIC Æ Realiza el envío del byte Dato desde el PC al PIC, según el
protocolo.
; PASO 1: Esperar a que el PIC esté listo para recibir el Dato (Estado de Reposo). ;El PIC estará listo
cuando al(PIC_LISTO) == 0
.repeat
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
13
Paralelo.asm
;;invocamos al procedimiento de lectura del puerto paralelo implementado en el fichero
invoke LeerPuerto,offset RegEstado ; registro de estado es donde se controla en que estado
se encuentra el puerto
and al,PIC_LISTO
.until !al
; PASO 2: El PC pone el Dato en el puerto (se lo envía al PIC), primero se pone el puerto de
salida; aunque normalmente no es necesario hacerlo, pues es el estado por defecto)
invoke LeerPuerto,offset RegControl ;se lee el registro del control para porder modificar su valor
cambiando solo los bits que nos hacen falta
mov ah,C5
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
mov eax,Dato
mov al,byte ptr [eax]
invoke EscribirPuerto,offset RegDato
; PASO 3: Indicación de que PC va a transmitir al PIC
invoke LeerPuerto,offset RegControl
mov ah,PC_LISTO
not ah
and al,ah
; Se pone a 0 al “PC_LISTO”, (aunque realmente en la linea a 1 ya que es lógica
inversa)
invoke EscribirPuerto,offset RegControl
; PASO 4: Esperar a que el PIC indique que ha recibido correctamente el Dato
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until al ; El PIC indica la toma del dato con al(PIC_LISTO) == 1
; PASO 5: El PC deja de indicar que transmite el Dato al PIC, pues se terminó la transmisión,
permitiendo que el PIC comience a trabajar
invoke LeerPuerto,offset RegControl
or al,PC_LISTO ; Se pone a 1 al “PC_LISTO”, (aunque realmente en la linea a 0 ya que es lógica
inversa)
invoke EscribirPuerto,offset RegControl
; PASO 6: Esperar a que el PIC esté de nuevo en Estado de Reposo, que es realmente de ejecución
del comando/instrucción. Este paso es opcional, pero es más seguro hacerlo.
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until !al ; El PIC estará en reposo (ejecutando el comando) cuando al(PIC_LISTO) == 0
; FINAL: Ahora el PIC procesará el comando/instrucción asociada al comando, mientras el PC espera
que el usuario solicite la ejecución de otro nuevo
ret
Enviar endp
En el siguiente diagrama se muestra todo el proceso a modo de diagrama de
flujo:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
14
3.1.3. Recepción desde el PC
Siguiendo la línea del envío desde el PC, tendremos la siguiente explicación:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
15
Recibir proc uses edx Dato:DWORD
; Recibe Dato desde PIC
; Realiza la recepción del byte Dato enviado desde el PIC, devolviéndolo en Dato, según el
protocolo.
; PASO 1: Poner PC en estado de envio (PC_LISTO == 1); a 0 en la linea
invoke LeerPuerto,offset RegControl
or al,PC_LISTO
invoke EscribirPuerto,offset RegControl
; PASO 2: Se pone el puerto de Datos de Entrada (Entrada/Salida)
invoke LeerPuerto,offset RegControl
or al,C5
invoke EscribirPuerto,offset RegControl
; PASO 3: Esperar a que el PIC indique el Dato está listo para recibirse
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until al ; El PIC indica que el dato está listo con al(PIC_LISTO) == 1 sin logica negada
; PASO 4: El PC lee el Dato
invoke LeerPuerto,offset RegDato
mov edx,Dato
mov byte ptr [edx],al
invoke LeerPuerto,offset RegControl
mov ah,C5
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
; PASO 5: El PC indica que ya recibió el Dato (PC_LISTO == 0)
invoke LeerPuerto,offset RegControl
mov ah,PC_LISTO
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
; PASO 6: El PC espera que el PIC deje de enviar el Dato (a que se de cuenta de que el PC ya
recibió el Dato)
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until !al ; El PIC finalizará la transmisión, con al(PIC_LISTO) == 0 sin logica negada
; PASO 7: Poner PC en estado de no reposo; a 0 en la linea
invoke LeerPuerto,offset RegControl
or al,PC_LISTO
invoke EscribirPuerto,offset RegControl
; FINAL: Ahora el PC ha recibido el Dato y lo procesará, mientras el PIC estará en reposo, sin
transmitir, pero podrá estar en la ejecución de un comando
ret
Recibir endp
Su diagrama de flujo será:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
16
3.1.4. Envío desde el PIC
En el envío de datos desde el PIC el protocolo empleado, que sería algo simétrico
y relacionado directamente con la recepción del PC, pues se deben producir
sincrónicamente, para lo cual se emplean bucles con condiciones en determinados
puntos, con las señales de control PC_LISTO y PIC_LISTO. A continuación vemos todo el
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
17
proceso con el siguiente diagrama de flujo, que equivale directamente a la explicación
conceptual de su funcionamiento.
La implementación de estos pasos es sencilla y directa, además de bastante corta,
pudiendo verse el código en el Anexo. Sólo cabe mencionar el hecho de que los bucles
se realizan sin etiquetas, si bien con etiquetas, tanto el funcionamiento como ocupación
del código una vez compilado serían el mismo; no obstante, se hace con saltos por
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
18
distancia para dar más legibilidad al código y menor tamaño pues son salto a la posición
inmediatamente anterior, es decir, a la instrucción previa (goto $-1).
El dato a enviar se indicará desde el programa que realiza la llamada en el
programa del PIC, poniendo dicho dato previamente en la variable global DatoPP, como
si se tratara de un parámetro de entrada a la función. Por otro lado, se mantiene el valor
que estaba en el puerto B en la variable DatoPORTB, para restaurar su valor tras la
recepción, asignándoselo a PORTB.
3.1.5. Recepción desde el PIC
En la recepción de datos desde el PIC el protocolo empleado, que sería algo
simétrico y relacionado directamente con envío desde el PC, pues se deben producir
sincrónicamente, para lo cual se emplean bucles con condiciones en determinados
puntos, con las señales de control PC_LISTO y PIC_LISTO. A continuación vemos todo el
proceso con el siguiente diagrama de flujo, que equivale directamente a la explicación
conceptual de su funcionamiento.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
19
La implementación de estos pasos es sencilla y directa, como en el caso del envío
desde el PIC, además de bastante corta, pudiendo verse el código en el Anexo.
El dato recibido se almacena en la variable global DatoPP. Como será visible
desde el exterior, se podrá conocer luego el valor del dato recibido y trabajar con él en el
PIC. Por otro lado, se mantiene el valor que estaba en el puerto B en la variable
DatoPORTB, para restaurar su valor tras la recepción, asignándoselo a PORTB.
3.1.6. Inicialización y Acceso al Puerto Paralelo desde el PC
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
20
Lo que vamos a hacer ene este apartado es describir de forma breve y clara en
que consiste la inicialización del puerto paralelo, así como los pasos que deberemos
tomar para tal objetivo, y para la interactuación con el mismo (Lectura / Escritura
Directa). Este archivo es paralelo.asm.
.386
.model flat,stdcall
option casemap:none
;se incluyen las librerias necesarias para permitir la lectura y escritura en el puerto gracias
al WinIo, la cual permite saltarse la protección de windows. Ademas se incluye el fichero
de sintaxis.
include Paralelo.inc
include WinIo.inc
includelib WinIo.lib
.const
;se inicializan los registros de los puertos paralelos que puede contener un ordenador
personal típico, asi se contribuye a la generalización.
LPT1
LPT2
LPT3
equ 0408h
equ LPT1+2
equ LPT2+2
.data?
;Expliquemos brevemente la funcion de cada reg del puerto.
REGDATO: en este registro se debe almacenar el dato a enviar
antes de dicha instrucción, y por otro lada es el registro
que deberemos leer una vez recibidos los datos, mediante una operación de recibir.
REGESTADO: este registro no se emplea en su totalidad sino que
se utiliza un único bit 6 (S6 o ACK), que usa logica
Positiva. Este bit se usa con PIC_LISTO.
REGCONTROL: en este registro pasa lo mismo que en el anterior,
simplemente utilizamos el bit 0 (C0 o ~Strobe) que
utiliza lógica inversa. Este bit se usa con el PC_LISTO, (verificar sincronismo). Tambien se
utiliza el bit 5 (C5), empleado para el control del puerto bidireccionado.
RegDato
RegEstado
RegControl
dw ?
dw ?
dw ?
.code
;se implementa un procedimiento para la obtención del puerto paralelo
TomarPuerto proc uses edx
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
21
; Con la llamada a InitializeWinIo se permite el acceso a Memoria y
Entrada/Salida directo.
; Luego se toma la dirección de los registros del puerto paralelo.
invoke InitializeWinIo
invoke GetPhysLong,LPT1,offset RegDato
mov dx,RegDato
inc dx
mov RegEstado,dx
inc dx
mov RegControl,dx
ret
TomarPuerto endp
;se implementa otro procedimiento para la implementación de la lectura desde el puerto
LeerPuerto proc uses edx Reg:DWORD
mov edx,Reg
mov dx,word ptr [edx]
in al,dx
ret
LeerPuerto endp
;y otro procedimiento mas para la escritura en el mismo
EscribirPuerto proc uses edx Reg:DWORD
mov edx,Reg
mov dx,word ptr [edx]
out dx,al
ret
EscribirPuerto endp
end
En el siguiente diagrama de bloques se muestra los que sería el proceso de
inicialización, descompuesto por pasos:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
22
3.1.7. Inicialización del PIC
En la inicialización se consigue preparar el PIC, y en concreto sus puertos para
una correcta comunicación con el PC, es decir, se podrán ejecutar las rutinas de envío y
recepción del PIC sin problema. La inicialización será, por tanto, necesaria antes de
realizar la primera operación de transmisión por el puerto paralelo, tanto si se trata de un
envío o una recepción. También será necesaria la inicialización si se han hecho
operaciones que hayan podido alterar el estado de los puertos respecto a la
configuración necesaria para enviar/recibir correctamente. Esto obliga a que en
ocasiones, las operaciones de enviar/recibir deban estar precedidas por una llamada a la
función de Inicializar.
En el siguiente diagrama de flujo queda patente la que sería la explicación
conceptual de su proceso, en el que se tiene PORTB de entrada y PORTA de salida,
exceptuando el bit de PC_LISTO, que estará como entrada.
Se observa igualmente, que al inicializar, se limpia PORTA (se pone a 0), teniendo
en cuenta que esta limpieza no afectará a PC_LISTO, por ser de entrada. Por su parte,
PORTB se restaura al valor de DatoPORTB que mantiene el valor que debe tenerse en el
puerto B. También es interesante el hecho de que se desactivan las cargas pull-up para
PORTB (donde este parámetro procede). La finalidad de esto es que por defecto el puerto
B está de entrada y el PC estará de salida, para mandar los comandos o cualquier otra
tarea. Es necesario realizar esto, dado que hay tareas externas al protocolo que pudieran
activar dichas cargas pull-up. Así, con todo esto se tiene lo que sería el Estado por
Defecto o de Reposo del PIC.
Finalmente, hay que mencionar que los valores por defecto de los bits de control,
aunque son modificables en tiempo de compilación, ocupan los dos bits más
significativos del puerto A (PORTA). En concreto, se están usando con la siguiente
configuración:
; Posiciones de los bits de control (en PORTA)
PIC_LISTO
equ 3
PC_LISTO
equ 4
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
23
Como ya se mencionó, PC_LISTO, que proviene del PC, será de entrada y
PIC_LISTO, al igual que el resto de bits de PORTA, serán de salida, como es lógico. Por
otro lado, aunque es característico del propio puerto paralelo, hay que decir que la señal
que se usa de PC_LISTO, en concreto la línea del puerto paralelo usada para ello (se
trata de ~STROBE), usa lógica negada, la cual se tiene en cuenta en el protocolo, y es un
aspecto muy importante en su implementación y entendimiento del protocolo. Por su
parte, la línea del puerto paralelo (se trata de ACK) usada para la señal de PIC_LISTO,
usa lógica normal, por lo que se tratamiento, entendimiento e implementación serán más
sencillas.
Por otro lado, aunque ya se han ido mencionado en las anteriores explicaciones,
tenemos dos variables globales. Se trata de DatoPP y DatoPORTB, definidos de la
siguiente forma y declarados como globales (global).
d_prot
udata
DatoPP
DatoPORTB
res 1
res 1
Como ya se ha dicho, DatoPP se usa para tomar el dato a enviar desde el PIC al
PC, o bien para recibir el dato enviado desde el PC al PIC (el que recibe el PIC). Y por
otro lado, DatoPORTB se encarga de salvar/mantener el valor del puerto B (PORTB), para
restaurarlo tras cada transmisión.
3.2. Intérprete Gráfico del PC (Emulador.exe)
En este apartado se explican de forma separada los diferentes puntos que forman parte
del programa del PC que funciona como intérprete de comandos para enviárselos al PIC, para
que éste los ejecute. Esto sería lo que entenderemos por emulador. La parte referente al
protocolo del puerto paralelo desde el PC hacia el PIC, como ya se comentó anteriormente por
separado, no se tratará aquí, si bien sí forma parte del programa gráfico del PC.
3.2.1. Traducción de Lenguaje Natural
A continuación vamos a explicar el proceso que seguimos para la traducción de
las instrucciones en lenguaje natural a código ensamblador que pueda entender el PIC.
Este proceso se desarrolla en dos pasos.
3.2.1.1. Fase 1: Traducción de Instrucciones
Un primer paso es el que se recoge en el fichero TraduceIns.asm. Este
consta de dos funciones: RellenaListasIns y RellenaEstructuras. Este primer paso se
lleva a cabo automáticamente al ejecutarse el programa. A continuación
mostramos sus interfaces y procederemos a explicarlas.
RellenaListaIns proc uses edi hInstance: HINSTANCE, hMainHeap: DWORD
hInstance: Es el manejador de nuestro fichero ejecutable.
hMainHeap: Es el manejador de la pila principal del programa. La
dirección de comienzo de la misma.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
24
RellenaEstructuras proc uses edi esi hHeap:DWORD, pBuffer:DWORD, nSize:DWORD,
ArrayOffset:DWORD, pArray:DWORD
hHeap: Es el manejador de la pila del programa. La dirección de
comienzo de la pila donde almacenaremos nuestras estructuras.
pBuffer: Puntero al buffer que contiene la ristra que vamos a tratar y a
partir de la cual crearemos la estructura.
nSize: Tamaño de dicha ristra.
ArrayOffset: Desplazamiento en el vector de colores
pArray: Puntero a la Matriz de Sintaxis
La función RellenaListasIns lo que hace es crear una matriz de estructuras
organizadas por su primera letra, a partir de un fichero con un formato
determinado. Cada estructura contiene información acerca de una instrucción en
lenguaje natural. Dichas estructuras tienen la siguiente forma:
INSTRUCCION struct
longitud dd ?
instruccion dd ?
pos db ?
nent db ?
nsal db ?
color dd ?
sig dd ?
INSTRUCCION ends
; Longitud de la instruccion para una comparación más rápida
; Nombre de la instruccion en lenguaje natural
; Número que identifica a la instrucción. Para acceder a ella
; Número de parámetros de entrada
; Número de parámetros de salida
; Puntero al Color
; Puntero a la siguiente estructura tipo instruccion
El parámetro ‘pos’, contiene un número que, a la hora de la compilación,
indicará al procedimiento encargado de enviar el comando al PIC, que comando
es el que desea mandarle mediante lo que sería un switch en lenguaje de alto
nivel.
Los colores que queremos utilizar están almacenados en un vector de
colores definido de la siguiente forma:
ASMColorArray dd 0h,0805F50h,0FFh,666F00h,44F0h,5F8754h,4 dup(0FF0000h)
El parámetro color, es un puntero a una de las posiciones de dicho vector.
A continuación mostramos el formato que debe seguir el fichero a partir
del cual se cargarán las estructuras.
Una cabecera Æ
[REGLAS]
Una instrucción en cada línea de la siguiente forma:
“Identificador” = “Nombre Instrucción” “Posición Instrucción” “Nº Param Entrada”
“Nº Param Salida”
Nuestro fichero es tiene este aspecto:
LeerPuertoA = LeerPuertoA 1 0 1
LeerPuertoB = LeerPuertoB 2 0 1
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
25
EscribirPuertoB = EscribirPuertoB 3 1 0
LeerTeclado = LeerTeclado 4 0 1
EscribirBus = EscribirBus 5 2 0
LeerBus = LeerBus 6 1 1
LeerNBus = LeerNBus 7 2 1
EscribirNBus = EscribirNBus 8 3 0
EnviarComandoLCD = EnviarComandoLCD 9 1 0
EnviarDatoLCD = EnviarDatoLCD 10 1 0
El proceso seguido para rellenar las estructuras es el siguiente:
1. Definimos los identificadores de nuestras instrucciones y de la cabecera del
fichero:
ASMSection db "REGLAS",0
I1Clave db "LeerPuertoA",0
I2Clave db "EscribirPuertoB",0
I3Clave db "LeerTeclado",0
I4Clave db "EscribirBus",0
I5Clave db "LeerBus",0
I6Clave db "EscribirNBus",0
I7Clave db "LeerNBus",0
I8Clave db "EnviarComandoLCD",0
I9Clave db "EnviarDatoLCD",0
I10Clave db "LeerPuertoB",0
2. Tomamos las ristras de formato del fichero utilizando la API de Windows
GetPrivateProfileString (que se encarga, dada una sección y una palabra
clave, de devolver el resto de la ristra asociada a esa palabra clave. Retorna
el nº de caracteres del la ristra). Así:
invoke GetPrivateProfileString,addr ASMSection,addr I1Clave,…,…,…,…
devolvería en el parámetro indicado un puntero a “LeerPuertoA 1 0 1”.
3. Ahora le pasamos a RellenaEstructuras dicha ristra que se encargará de
almacenar en una estructura tipo INSTRUCCIÓN como la que mostramos
antes. Para rellenar cada estructura se realiza un simple tratamiento de ristras.
Los pasos que se seguimos son:
3.1.
Leemos la ristra pasada hasta el primer espacio y almacenamos en la
estructura INSTRUCCIÓN el nombre de la misma, la longitud y el color.
(El color se va asignando por orden en el vector, así a la primera
instrucción se le asigna el color 1, a la segundo el 2 y sucesivamente.
3.2.
Utilizando el nombre de la instrucción, comprobamos si en la Matriz
de Sintaxis, existe ya una instrucción que empiece por dicha letra. Si no es
así, pues almacenamos en la matriz un puntero a dicha estructura. En
caso afirmativo lo que hacemos es que, el puntero de la matriz que
apuntaba a la estructura cuya instrucción empezaba por la misma letra,
lo cambiamos a nuestra nueva estructura; y el campo siguiente de
nuestra nueva estructura hacemos que apunte a la que acabamos de
quitar. Veámoslo gráficamente para una mayor comprensión.
Inicialmente vamos a introducir nuestra primera estructura
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
26
LeerPuertoA
pos
long
nent
nsal
col
sig
MATRIZ DE SINTAXIS
null
null
null
null
null
null
null
Al no haber ninguna instrucción en la matriz de sintaxis que empiece por L, la
insertamos en la primera posición que encontremos libre.
MATRIZ DE SINTAXIS
null
null
null
null
null
null
LeerPuertoA
pos
long
nent
nsal
col
sig
A continuación introduciremos la siguiente instrucción.
LeerPuertoB
pos
long
nent
nsal
col
sig
MATRIZ DE SINTAXIS
null
null
null
null
null
null
LeerPuertoA
pos
long
nent
nsal
col
sig
Al ya existir una estructura cuya instrucción empieza por la letra L el resultado final
sería:
MATRIZ DE SINTAXIS
null
null
null
null
null
null
LeerPuertoB
3.3.
pos
Long
nent
nsal
col
sig
LeerPuertoA
pos
long
nent
nsal
col
sig
Por último, leyendo de espacio en espacio, rellenamos los campos
pos, nent, nsal, que contendrán la posición de la instrucción a la hora de
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
27
traducirla y el número de parámetros de entrada y de salida
respectivamente.
3.2.1.2. Fase 2: Compilación de Instrucciones
El proceso de compilación, recogido en el fichero Compila.asm se basa
en tres funciones con la siguiente interfaz:
Compila proc uses edx hWnd:DWORD,hREd:DWORD,hHeap:DWORD
hWnd: Manejador de la ventana (Window)
hREd: Manejador del cuadro de texto enriquecido (RichEditText)
hHeap: Manejador de la pila (Heap)
CompilaLinea proc uses esi edi Linea:DWORD
Linea: Puntero a la ristra que contiene la instrucción a tratar
Ejecutar proc uses edx Comando:DWORD, Entrada:DWORD, Salida:DWORD
Comando: Es la posición de la instrucción que indicará el
comando a enviar al
PIC.
Entrada: Puntero al vector de parámetros de Entrada
Salida: Puntero al vector de parámetros de Salida.
Dicho proceso se fundamenta en 6 pasos:
1. Se lee la primera línea que es la que contendrá las definiciones de las
variables a utilizar, y se almacenan estas en memoria en forma de listas de
estructuras del tipo VARIABLES. Para ello utilizaremos dos funciones cuyas
interfaces detallamos a continuación:
VARIABLES struct
nombre dd ?
valor db ?
sig dd NULL
VARIABLES ends
;puntero al nombre de la variable ASCII
;puntero a la sig estructura
ListaVariables proc uses edi hHeap:DWORD,Buffer:DWORD
hHeap: Manejador de la Pila del programa
Buffer: Buffer con la ristra de variables a tratar
InsertarVariable proc uses edi hHeap:DWORD, VarActual:DWORD, pVarSig:DWORD
hHeap: Manejador de la Pila del programa
VarActual: Dirección donde esté almacenada la ristra de la
variable actual (se pasa por valor, es de entrada)
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
28
pVarSig: Puntero a la variable siguiente (se pasa por puntero, es
de entrada/salida)
eax: Devuelve puntero a la estructura de la variable
El mecanismo para la creación de listas de variables es parecido al que
utilizamos para la creación de la lista de instrucciones pero más modulado lo que
conlleva una mejor comprensión:
1.1.
El primer paso dentro del procedimiento ListaVariables es separar la
directiva de variables (var) y la primera variable de la línea, para a
continuación comprobar si en efecto coincide con la cabecera de
definición de variables (idVar). Todo lo esto se hace con un mero
tratamiento de ristras.
1.2.
Procedemos a insertar todas las variables en la lista. Para ello nos
posicionamos en la siguiente variable y se la pasamos como parámetro a
la función InsertarVariable. Esta función creará una estructura del tipo
VARIABLES a partir del puntero a la variable actual y devolverá en eax la
dirección de la misma.
1.3.
Cuando se retorne desde InsertarVariable a ListaVariables se
actualizará el campo sig de la variable anterior y se hará que apunte a la
nueva estructura creada.
1.4.
De este modo se irá creando la lista hasta llegar a la última variable,
que estará delimitada por el retorno de carro.
2. Una vez almacenadas las variables a utilizar, se irá compilando línea a línea,
es decir, instrucción a instrucción, mediante un bucle que irá leyendo línea
por línea y pasándoselo como parámetro a un procedimiento denominado
CompilaLinea.
3. Cuando CompilaLinea recibe una instrucción lee primeramente el nombre de
la misma y la busca en la Matriz de Sintaxis que habíamos confeccionado
anteriormente. Una vez la encuentra, pasa la información que contiene la
estructura de la instrucción a variables locales (el número de parámetros de
entrada y salida, y la posición de la instrucción).
4. A partir de estos datos, sabiendo que cada parámetro en las instrucciones está
separado por el espacio en blanco y que primero aparecen los parámetros de
entrada y luego los de salida, los va leyendo uno a uno y almacenándolos en
sendos vectores de parámetros de entrada y de salida. El vector de
parámetros de salida contiene realmente punteros a las zonas de memoria
donde debemos almacenar dichos valores. Veamos como tratamos cada uno:
4.1.
Parámetros de Entrada: En los parámetros de entrada pueden ocurrir
dos cosas: que sea un valor numérico o que sea una variable. Para
diferenciar una de otra, estudiamos el primer carácter del parámetro. Si
es un número, lo asumimos como un valor y si es una letra como una
variable. En caso de que sea una variable, la buscamos en la lista
confeccionada anteriormente, leemos el valor que tiene, y lo
almacenamos en el vector de parámetros de entrada.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
29
4.2.
Parámetros de Salida: Una vez leída la variable de la que se trata,
copiamos al vector de parámetros de salida la dirección de donde se va
a almacenar el valor de esa variable.
5. Es en este momento, una vez ya obtenidos los parámetros necesarios, cuando
llamamos al procedimiento Ejecutar pasándole como parámetros la dirección
de los vectores de entrada y salida y la posición de la instrucción a ejecutar.
Este, mediante una estructura selectiva mandará al PIC la instrucción
correspondiente utilizando las funciones de Enviar y Recibir definidas en
Protocolo.asm.
6. Una vez interpretadas todas las líneas acabaría el proceso de compilación.
3.2.2. Manejo del Programa Gráfico
En este apartado lo que se intenta conseguir es una descripción apta desde el
punto de vista de usuario sobre el manejo de El Emulador. Pues bien, brevemente
detallaremos de que consta el entorno gráfico de este programa. En primer lugar tenemos
una barra de tareas como la que se muestra en la siguiente figura.
Como se puede comprobar a parte del típico botón de ayuda, tenemos cinco
menús con lo que podremos interactuar de manera sencilla.
El primer menú que aparece es el de Archivo, el cual es común a todos los
programas de entorno Windows, y cuyas características principales son:
Nuevo. Creación de un nuevo archivo.
Abrir. Apertura de un archivo creado con anterioridad.
Guardar/ Guardad como… Permite salvar un archivo modificado o recién creado.
Imprimir. Impresión del archivo actual.
Salir. Salir de la ejecución del programa.
En el siguiente menú nos encontramos con las típicas operaciones que podemos
hacer con el fichero actualmente cargado, en lo que se refiere a la edición como copiar,
cortar, pegar, así como la búsqueda de palabras claves, etc. Y que como es obvio no
vamos a detallar de una forma más amplia.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
30
Este sin duda es el menú más importante, y en consecuencia el que más nos
atañe, Proyecto. Pues bien, este menú consta de:
Compilar: realiza la compilación del archivo actual para conseguir el código hexadecimal
que se cargara en el pic, para su ejecución. Esto se consigue gracias a la utilización del
fichero compila.asm. Este archivo toma las variables declaradas (ya se dirá luego como se
hace) y ejecuta cada linea que contiene comandos válidos, asociando cada variable a su
valor.
Compilar linea: su tarea es la misma que la de compilar, el único detalle es que sólo
complia la línea seleccionada. Esta opción da mucha libertad a la hora de compilar
trozos de código, y hacer una especie de depuración manual del archivo.
Enviar Dato: su objetivo primordial es la comunicación del PC con el microcontrolador,
de tal forma que se envia un valor enterto que se mostrará en el puerto B del PIC. Esta
opción es una instrucción básica.
Recibir Dato: realiza la misma labor que la opción enviar dato, pero en sentido opuesto.
Ahora es el PIC quien envia el dato y el PC quien lo recibe. Una vez obtenido el dato se
muestra una caja de texto, con el valor leido del puerto paralelo.
Testear Puerto: esta función lo que hace es comprobar si la comunicación del PC y el
microcontrolador a través del puerto paralelo es válida, dicho de otro modo, si el puerto
esta listo para recibir y enviar datos. A continuación mostraremos la implementación del
código referente a este apartado y cuyo código se encuentra en protocolo.asm.
TestPuerto proc uses eax ecx edx
;----------------------------------------------;
Bucle de testeo para el puerto Paralelo
;-----------------------------------------------
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
31
; Direcciones standard del puerto paralelo:
; Dato --> 378h
; Estado --> 379h
; Control --> 37ah
;----------------------------------------------mov cx,1
; Valores por defecto para puerto (dx) y dato (al)
mov dx, RegDato
mov dx, RegEstado
mov dx, RegControl
mov al,20h
; 20h activaria C5 en RegControl
.while cx==1
out dx,al
in al,dx
.endw
ret
TestPuerto endp
Por último quedan dos menús que no tienes demasiada importancia, como lo son
el menú ver y el menú opciones.
Pues bien, una vez explicados cada menú y señalado donde se puede realizar
cada operación, lo siguiente que vamos a explicar será la creación de programas
emulados, que nuestro programa será capaz de interpretar y compilar, para su ejecución
emulada desde el PIC.
3.2.2.1. Creación de Programas emulados
Para la creación de programas emulados debemos tener claros los
siguientes aspectos:
Declaración de variables.
Las variables no son más que un nombre y un valor asociado. El nombre
sirve para que al analizar el texto podamos distinguirla. Las variables se declaran
con la palabra clave var <espacio>, a continuación se declaran las variables que
vamos a emplear separadas por espacios. Solo se diferencian de los números por
ese hecho (que son números), ya que su implementación es igual.
EJEMPLO: se usa de la siguiente forma: se escribe en la primera línea la
definición de variables:
var a b c
Parámetros de las instrucciones.
Las instrucciones tienen sus propios parámetros, y estos van separados por
espacios. Las instrucciones que se pueden implementar son las siguientes:
INS PosInstr NºParamEnt NºParamSal
[REGLAS]
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
32
LeerPuertoA= LeerPuertoA 0 0 1
LeerPuertoB= LeerPuertoB 1 0 1
EscribirPuertoA= EscribirPuertoA 2 1 0
EscribirPuertoB= EscribirPuertoB 3 1 0
LeerTeclado= LeerTeclado 4 0 1
EscribirBus= EscribirBus 5 2 0
LeerBus= LeerBus 6 1 1
EscribirNBus= EscribirNBus 7 3 0
LeerNBus= LeerNBus 8 2 1
EnviarDatoLCD= EnviarDatoLCD 9 1 0
EnviarComandoLCD= EnviarComandoLCD 10 1 0
Mostrar= Mostrar 11 1 0
Asignar= Asignar 12 1 1
; Comandos del LCD; se deben ejecutar
EnviarComandoLCD
[COMANDOSLCD]
LimpiarDisplay= LimpiarDisplay 0 0 0
IrAInicio= IrAInicio 1 0 0
Modo= Modo 2 2 0
DisplayOnOFF= DisplayOnOFF 3 3 0
Desplazamiento= Desplazamiento 4 2 0
FunctionSet= FunctionSet 5 3 0
DireccionCGRAM= DireccionCGRAM 6 1 0
DireccionDDRAM= DireccionDDRAM 7 1 0
Estado= Estado 8 0 2
EscribirRAM= 9 1 0
LeerRAM= 10 0 1
siempre
y
obligatoriamente
después
de
En cada línea sólo se podrá especificar un comando. Un ejemplo de esta
implementación sería:
var a
; se declara la variable a (se inicializan siempre a 0)
LeerPuertoB a ; el valor devuelto se asigna a la variable a (parámetro de salida)
Mostrar a
; toma el valor de la variable a (parámetro de entrada) y lo
muestro en messageBox (si no está implementada hay que implementarla, me
refiero a la función Mostar, que sería Mostrar 1 0 en el fichero de sintaxis, pues
tiene 1 parametro entrada y 0 parametro salida)
Otros ejemplos serían:
Ejemplo 1:
var a
LeerPuertoA a
Mostrar a
LeerPuertoB a
Mostrar a
LeerTeclado a
Mostrar a
LeerBus 112 a
Mostrar a
Ejemplo 2:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
33
var a
LeerTeclado a
EnviarDatoLCD "Ha pulsado: "
EnviarDatoLCD a
LeerTeclado a
EnviarComandoLCD 1
EnviarDatoLCD "Ha pulsado: "
EnviarDatoLCD a
3.3. Programa de Emulación de Instrucciones del PIC
El programa de emulación de instrucciones del PIC es una versión mejorada y adaptada
al protocolo de puerto paralelo del programa desarrollado para el mismo fin por el grupo que
hizo una práctica similar en años previos.
Como se ha dicho las tareas fundamentales han sido la adaptación a una filosofía de
comunicación paralela, que conlleva el hecho del uso de más líneas, en concreto todo el puerto
B y dos líneas de control de puerto A. Esto habrá que tenerlo en consideración al realizar la
implementación de este programa, por lo que habrá que adaptarlo. Igualmente, se ha
aprovechado para hacer que el programa de emulación sea más compacto, es decir, conseguir
que ocupe menos en la memoria del PIC, y que se ejecute más rápido.
Para empezar, el programa recibe el comando a ejecutar y bifurca a una zona encargada
de procesarlo. A continuación se puede ver como la bifurcación se implementa relativa al PCL.
En cuanto ha esto hay que mencionar el hecho de que se tiene que tener cuidado con el valor de
el registro de control de página (PCLATH). Este valor es modificado por la función de conversión
de las teclas del teclado. Por ello se salva el valor de PCLATH para luego restaurarlo y evitar
problemas.
LeerComando
call Inicializar
call Recibir
; Recibir Comando
movfw DatoPP
addwf PCL
goto leerA
; 0 --> leer del puerto A
goto leerB
; 1 --> leer del puerto B
goto escribirA
; 2 --> escribir en puerto A
goto escribirB
; 3 --> escribir en puerto B
goto leerteclado
; 4 --> leer teclado hexadecimal
goto escribir_1_byte ; 5 --> escribir un solo byte en bus I2C
goto leer_1_byte
; 6 --> leer un solo byte del bus I2C
goto escribir_n_bytes ; 7 --> escribir "n" bytes en bus I2C
goto leer_n_bytes
; 8 --> leer "n" bytes del bus I2C
goto env_dato_lcd ; 9 --> enviar dato al LCD
goto env_com_lcd ; 10 --> enviar comando al LCD
goto terror
; ERROR --> si no es ningún comando
A continuación, se muestran explicaciones breves de cada uno de los comandos que
procesa el PIC.
0. Leer Puerto A.
Simplemente se pone PORTA de entrada, se lee su valor y se envía al PC.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
34
1. Leer Puerto B.
Se pone PORTB como salida y se le asigna el valor del mismo, almacenado en
DatoPORTB. Una vez hecho esto se toma su valor y se envía al PC.
2. Escribir Puerto A.
Simplemente se recibe del PC el dato a escribir, se asegura que PORTA esté de salida y se
escribe en PORTA.
3. Escribir Puerto B.
Simplemente se recibe del PC el dato a escribir, se asegura que PORTB esté de salida y se
escribe en PORTB, y a su vez se salva su valor en DatoPORTB para que no se pierda tras el uso
del protocolo del puerto paralelo.
4. Leer Teclado.
Para la lectura de teclas de teclado, para lo cual se usa la librería tecla.inc, se hace uso
de la rutina Key_Scan, que detecta la tecla que haya sido pulsada; en caso de no haberse
pulsado ninguna tecla devuelve el código 0x80. De este modo en le rutina del comando
LeerTeclado se hace un bucle en el que se llama a Key_Scan hasta que devuelva una tecla
pulsada (un código distinto de 0x80).
Una vez se tiene el código de la tecla pulsada se convierte a ASCII con la rutina (también
de la librería tecla.inc) Cods_Tecla. Esta rutina usa tablas referenciadas por el registro PCL y se
hace uso de la directiva de precompilación PAGESEL, de forma que hay una posible modificación
del registro indicador de la página actual (PCLATH). Este hecho obliga a salvar el valor de
PCLATH antes de la llamada a Cods_Tecla y restaurar su valor después de la llamada.
Una vez se tiene el valor ASCII de la tecla pulsada, que ocupa 8 bits, es decir, su tamaño
es el mismo que el número de líneas de PORTB. Esto permite que no se requiera ningún
tratamiento especial a la hora de enviarlo. Así, se envía al PC, tras Inicializar antes, para
reconfigurar el estado de los puertos.
5. Escribir 1 byte en el Bus I2C.
Para este comando se emplea la librería i2c.inc. En este comando en concreto se usa la
función ENVIAR_1, que envía/escribe un dato en una dirección del bus I2C. De este modo, se
recibe del PC el dato y la dirección, y luego se envía al bus I2C, proceso tan simple como llamar
a ENVIAR_1 tras haber configurado bien los parámetros de entrada DATO (con el dato recibido)
y DIRECCION (con la dirección recibida).
6. Leer 1 byte en el Bus I2C.
Para este comando se emplea la librería i2c.inc. En este comando en concreto se usa la
función RECIBIR_1, que recibe/lee un dato en una dirección del bus I2C. De este modo, se recibe
del PC la dirección, y luego se recibe del bus I2C el dato presente en ella, proceso tan simple
como llamar a RECIBIR _1 tras haber configurado bien el parámetro de entrada DIRECCION (con
la dirección recibida). Finalmente, el dato leído se envía al PC.
7. Escribir N byte en el Bus I2C.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
35
Para este comando se emplea la librería i2c.inc. En este comando en concreto se usa la
función ENVIAR, que envía/escribe un determinado número de datos a partir de una dirección del
bus I2C. De este modo, se recibe del PC el número de bytes a escribir y la dirección de partida.
El siguiente paso consiste en un sencillo bucle que recibe del PC el dato a escribir en el
bus I2C (tras Inicializar, para reconfigurar los puertos para la transmisión por el puerto paralelo) y
lo envía/escribe en el bus I2C con la función ENVIAR. Además, en el bucle se controlará que se
envíen tantos datos como se hayan indicado, tarea que también es controlada por la función
ENVIAR, pues con la variable CONT_BYTES sabe cuantos bytes quedan por enviarse al bus I2C
(funciona como variable global de estado del bus I2C). Por otro lado se siguen usando DATO y
DIRECCION, como con ENVIAR_1.
8. Leer N byte en el Bus I2C.
Para este comando se emplea la librería i2c.inc. En este comando en concreto se usa la
función RECIBIR, que recibe/lee un determinado número de datos a partir de una dirección del
bus I2C. De este modo, se recibe del PC el número de bytes a leer y la dirección de partida.
El siguiente paso consiste en un sencillo bucle que se llama a RECIBIR para leer el dato
del bus I2C y luego se envía al PC (tras Inicializar, para reconfigurar los puertos para la
transmisión por el puerto paralelo). Además, en el bucle se controlará que se envíen tantos datos
como se hayan indicado, tarea que también es controlada por la función RECIBIR, pues con la
variable CONT_BYTES sabe cuantos bytes quedan por recibirse al bus I2C (funciona como
variable global de estado del bus I2C). Por otro lado se siguen usando DATO y DIRECCION,
como con RECIBIR_1.
9. Enviar Dato LCD.
En este caso la librería empleada es lcd.inc. La filosofía de su uso consiste en entrar en un
bucle de envío de datos al LCD, usándose el valor 0 como indicador del final del envío de datos
al LCD, momento en el que se abandonará la rutina de env_dato_lcd.
De este modo, en el bucle se empieza por recibir el dato del PC para mandarlo al LCD. A
continuación se llama a la función DISPLAY_CONF, que inicia el LCD configurándolo
correctamente para que no se vea el cursor, tenga autoincremento; en cuanto al foco se
mantendrá el anterior, entendiendo por ello la última posición en que estaba, normalmente en la
primera línea del LCD.
Así, a continuación se comprueba que el dato recibido no sea un 0, en cuyo caso se
abandona la rutina, y se manda el dato al LCD con la función LCD_DATO, poniendo el dato a
enviarle en el acumulador W previamente, como parámetro de entrada.
10. Enviar Comando LCD.
Como en la rutina previa, se usa la librería lcd.inc. En este caso la rutina es mucho más
sencilla y bastará con empezar recibiendo del PC el comando a mandar al LCD y posteriormente
llamando a la función LCD_REG poniendo en el acumulador W (como parámetro de entrada) el
comando que ejecutará en el LCD.
Finalmente, en caso de que no sea un comando válido, fenómeno que no deberá ocurrir
porque el PC conoce los comandos posibles y no provocará el error, se bifurca a una rutina de
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
36
error en la que el PIC queda parado. Podría intentarse un diagnóstico de errores, e incluso
recuperarse, pero en principio puede ser una tarea compleja, pues es necesario obtener un
estado de reposo en PIC y PC, “matando” las tareas de envío/recepción tanto en el PIC como en
el PC.
3.4. Rutinas de Retardo para el PIC
Para facilitar la tarea de temporizaciones exactas en el PIC se ha desarrollado una librería
para las mismas. Las rutinas de temporización que podrán usarse son las siguientes:
1. Retardo de 1 bucle.
Se puede crear un retardo de 6 a 768 microsegundos. Se controla con la variable global
N1, que obedece a la siguiente ecuación: 3*(N1+1) + 2 = X microsegundos. Hay que añadir
que en esa ecuación se tienen en cuenta los 2 microsegundos que se pierden en la asignación de
un valor a N1.
2. Retardo de 2 bucles.
Se puede crear un retardo de 16 a 197628 microsegundos. Se controla con las variables
globales N1 y N2, que obedece a la siguiente ecuación: 3*N2*(N1+3) + N2 + 3 + 4 = X
microsegundos. Hay que añadir que en esa ecuación se tienen en cuenta los 4 microsegundos
que se pierden en la asignación de un valor a N1 y a N2.
3. Retardo de 3 bucles.
Se puede crear un retardo de 26 a 50396928 microsegundos. Se controla con las
variables globales N1, N2 y N3 que obedece a la siguiente ecuación: 3*N3*N2*N1 +
10*N3*(N2+1) + 3 + 6 = X microsegundos. Hay que añadir que en esa ecuación se tienen en
cuenta los 6 microsegundos que se pierden en la asignación de un valor a N1, N2 y a N3.
4. Retardo de 1 milisegundo.
Genera directamente un retardo de 1 milisegundo (RetardoMS).
5. Retardo de 1 centésima de segundo.
Genera directamente un retardo de 1 centésima de segundo (RetardoCS).
6. Retardo de 1 décima de segundo.
Genera directamente un retardo de 1 décima de segundo (RetardoDS).
7. Retardo de 1 segundo.
Genera directamente un retardo de 1 segundo (RetardoS).
3.5. Fichero Cabecera para el PIC
Por fichero cabecera para el PIC se entiende el fichero creado bajo el nombre de
InstrPIC16F84.h. Este fichero se ha creado para que sea el único que deba incluirse al crear
ficheros para programas del PIC16F84. Se basa en el fichero Instr.h ya creado para la Práctica 1,
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
37
por lo que no será necesario comentarlo a fondo, sino que a continuación se enumeran grosso
modo sus utilidades fundamentales:
1. Inclusión de fichero de macros Standard del PIC16F84.
Equivale simplemente a las típicas instrucciones de:
processor PIC16F84
#include <p16f84.inc>
2. Inhibición de Mensajes y Avisos.
Se trata de que se puede escribir el código con las tabulaciones deseadas sin que el
compilador muestre sus avisos. Se usa la cláusula errorlevel.
3. Directivas y Macros para control de Entrada/Salida de los Puertos.
Se resume a las directivas Sal y Ent, y la macro Mod_Tris.
4. Macros para Relojes.
Se trata de las macros para detener e inicializar el reloj del PIC (timer).
5. Operaciones Aritméticas, Manejo de Registros y Control de Flujo.
Se trata de compactar las operaciones aritméticas y de manejo de registros para poder
hacer de registro a registro, en lugar de referidas al acumulador W. Se implementan con macros.
Igualmente se tiene instrucciones típicas de control de flujo a modo de condiciones de un if
condición then.
6. Salvado y Restauración de Registros.
Se trata de la emulación típica de instrucciones tipo PUSH y POP; en este caso salvan el
acumulador y registro de estado.
Finalmente, hay que decir, que bastará con poner en nuestros códigos la inclusión de este
fichero cabecera, de la siguiente forma:
#include "Lib\InstrPIC16F84.h"
3.6. Directivas especiales de Ensamblador MASM32
Al usar en lenguaje ensamblador MASM32 (Microsoft® Macro Assambler de 32 bits),
específico para entorno gráfico de Win32 en máquinas con plataformas Windows® que
funcionarán con modo protegido de ejecución de los programas. El concepto de modo protegido
de ejecución se tendrán programas que al ejecutarse sólo podrán ejecutar instrucciones de
propósito general, mientras que el set de instrucciones privilegiadas no podrán ejecutarlas, pues
los programas se ejecutan en modo usuario y es necesario que primero pasen a modo protegido
para poder hacerlo. De forma general, no es necesario pasar al modo protegido, de hecho no
tenemos tal derecho. Sin embargo, podremos usar el numeroso número de funciones de la API
de Windows® que permiten el acceso a numerosos recursos, cuyo acceso real se hará de forma
controlada por el Sistema Operativo.
Así, se tendrán que usar directivas especiales para indicar el tipo de programa que
diseñaremos, pues en ocasiones podemos incluso crear programas para ejecución en modo
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
38
privilegiado. Por lo general podemos definir el modo de direccionamiento y demás características
del modo de ejecución de nuestro programa que se explican brevemente a continuación, de
forma categorizada. Igualmente, con MASM32 tenemos la posibilidad del uso del típico precompilador (que no se explicará por ser ampliamente conocido), pero además de una
herramienta superior a las macros, que permiten el uso de sentencias de control de flujo, cuyo
funcionamiento y traducción a ensamblador puro es inmediata. Mientras las macros sustituyen
código en una zona contigua, estas directivas permitirán situar código en puntos estratégicos,
como se explicará más adelante. Otro aspecto importante que se permite y comentará a
continuación es la posibilidad de llamar a función con paso de parámetros, con equilibrado
automático de la pila del programa.
1. Directivas de Tipo de Programa.
Todo programa en ensamblador para entorno Win32 debe tener una estructura como la
siguiente:
.386
.model flat,stdcall
option casemap:none
El significado de las directives anteriores sería el siguiente:
1.1.
Tipo de lenguaje o equipo.
Se trata de definir el tipo de máquina que usaremos, que por lo tanto tendrá un lenguaje
ensamblador similar al de todos los Pentium y 80x86, pero de una versión a otra aparecen
nuevas instrucciones y diferencias en el funcionamiento del procesador y el modo en que la
máquina ejecuta los programas, desde la pila hasta el procesador, pues ciertas tareas pueden
realizarse de forma distinta.
El tipo de máquina más común cuando se programa en entorno gráfico Win32 es el .386,
ya que permite usar entorno gráfico de Windows con compatibilidad para prácticamente todos
los sistemas.
1.2.
Modelo de Direccionamiento y Paso de Parámetros.
Con la directiva .model se indica el tipo de direccionamiento de memoria. Prácticamente
siempre se usa el modo flat (modo plano). En este modo se puede direccional toda la memoria
directamente, desaparece el concepto de segmento, de modo que podemos direccional los 4 Gb
posibles de memoria, con los 32 bits de los registros.
Por otro lado, en cuanto al paso de parámetros se puede indicar el modo en que se hará
en las llamadas a las rutinas que se creen. Esto define el sentido de paso de los mismos y quién
se encargará de compensar la pila, bien el programa llamado o el llamador. El modo típico es el
stdcall, que sería el modo Standard de llamada, en el que se llena la pila con los parámetros
ordenados desde el último al primero (de derecha a izquierda) y es el llamado el encargado de
compensar la pila tras la llamada, en el retorno de la subrutina. En Win32 siempre se usa el
modo stdcall, si bien hay una excepción con la función wsprintf(), que es conocida por admitir
cualquier número de parámetros, y por ello se requiere el convenio inverso en el paso de
parámetros.
1.3.
Opciones para MASM.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
39
Finalmente, con la directiva option, se puede indicar al MASM que compila o funciones en
distintos modos. Así, por ejemplo, con option casemap:none, se le indica que las etiquetas sean
sensibles a mayúsculas y minúsculas.
2. Directivas de Control de Flujo.
Las directivas de control de flujo son herramientas propias de lenguajes de alto nivel, pero
en el lenguaje ensamblador MASM se dispone de ellas. Sin embargo, esto no significa que
estemos usando un lenguaje de alto nivel, pues de hecho no hay optimización de código (más
que la que muchos compiladores ya usan, en casos muy concretos). De hecho, las directivas de
control de flujo usadas con MASM funcionan como si fueran unas macros más potentes, que
ponen el código necesario no sólo en la zona donde se escribe la directiva, sino en otras zonas
no contiguas, pero muy evidentes. De este modo, aunque se usan estas directivas de control de
flujo, siempre sabemos en que instrucciones se traduce nuestro programa; además, esto es
fundamental, pues el código final será ensamblador puro y de no saber en que se traducen
dichas directivas no sabremos depurar, ante la incapacidad de entender el código, pero como ya
se ha mencionado será muy sencillo. A continuación se muestra la conversión que se hace de las
directivas de control de flujo a ensamblador puro, para cada una de ellas.
2.1.
Si Condicional.
Una estructura condicional tipo .if puede admitir, en su forma más compleja, la siguiente
forma:
.if condiciónIF1
;códigoIF1
.elseif condiciónIF2
;códigoIF2
.else
;códigoIELSE
.endif
En primer lugar hay que tener en cuenta que las condiciones que pueden tenerse serán de
muy diversos tipos y por ello se traducirán de distintas formas. Por lo general se tiene una
comparación acompañada de un salto condicional en función del tipo de comparación. A
continuación se muestra una serie de equivalencias que cubren la mayoría de los casos:
eax==1
eax>1
!eax
eax!=0
eax
eax==0
se traduce como
se traduce como
se traduce como
se traduce como
se traduce como
se traduce como
cmp eax,1
cmp eax,1
or eax,eax
or eax,eax
or eax,eax
or eax,eax
Se observa en primer lugar que las condiciones eax y eax==0 son equivalentes (al igual
que las versiones negadas). Así, las comparaciones que tienen un 0, por lo general, no requieren
el operador cmp. Por otro lado, para tratar el dominio que deja a un lado u otro una
comparación, se usarán los saltos, y dependerá su uso de la función que se pretenda hacer, es
decir, a veces interesa usar para saltar y otras para no saltar. En los anteriores casos bastará usar
jz y jnz, además jbe para la comparación eax>1.
Volviendo con la sentencia .if, su traducción a ensamblador puro será:
condiciónIF1
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
40
jxx ELSEIF1
;códigoIF1
jmp finIF
ELSEIF1:
condiciónIF2
jxx ELSE
;códigoIF2
jmp finIF
ELSE:
;códigoIELSE
finIF:
Este esquema se repetirá tantas veces como sea necesario en función del número de
sentencias de tipo .elseif.
2.2.
Bucles While.
El formato tipo de una sentencia con .while sería:
.while condiciónWHILE
; código
.break
.break .if condiciónBREAK
.endw
Suponiendo una condición normal, el compilador traduce la comparación. Por ejemplo,
para eax != 0 simplemente haría falta hacer:
or eax,eax
jnz seguir
Como se observa el compilador hará la mejor traducción de la comparación posible
haciendo uso de una operación aritmética y un salto condicional en la mayoría de los casos.
Para la implementación en ensamblador puro del bucle del ejemplo tendríamos un
código como el siguiente (hecho con etiquetas para darle mayor comprensibilidad):
jmp fin
inicio:
; código
jmp salir
condiciónBREAK
jxx salir
fin:
salir:
condiciónWHILE
jxx inicio
A parte del hecho de que los saltos de las condiciones dependerán de la misma, se
observa que el bucle comienza con un característico salto a una posición final en la que está la
condición, para evaluarla la primera vez. Además, luego se comprobará directamente al llegarse
al final del bucle. Si se cumple la condición del bucle se va a la primera instrucción del bucle.
En cuanto a los puntos de ruptura (.break) interiores al bucle, si se llega a ellos se salta
directamente fuera del bucle. Si son puntos de ruptura condicionales (.break .if condiciónBREAK)
primero se evalúa la condición y luego se saldrá del bucle si procede.
En el caso de bucles con condiciones para bucles infinitos, del estilo:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
41
.while TRUE
; código
.endw
El compilador identifica que es un bucle infinito y hace la traducción sin condición, de
forma que en el caso anterior tenemos:
inicio:
salir:
; código
jmp inicio
Como es lógico, habrá puntos de ruptura, para salir de este tipo de bucle.
2.3.
Bucles Repeat.
De forma similar a los bucles .while, en el caso de los bucles .repeat
, su forma será:
.repeat
; código
.break
.break .if condiciónBREAK
.until condiciónREPEAT
En este caso, no será necesario explicar las sentencias de punto de ruptura, ya
mencionadas anteriormente. En el caso de los bucles .repeat, siempre se entra una vez, por lo que
no hay que evaluar la condición por primera vez. Además, la evaluación de la condición tiene el
sentido contrario al que tiene en los bucles .while. Esto significa que si la condición no se cumple
se sigue iterando, pero si sí se cumple se saldrá. Así, el código ensamblador puro equivalente
será:
inicio:
; código
jmp salir
condiciónBREAK
jxx salir
salir:
condiciónREPEAT
jxx inicio
3. Funciones con paso de parámetros.
Cuando usamos funciones en MASM32 se llamarán de la siguiente forma, como
alternativa más cómoda al call:
invoke funcion, parametro1, parametro2, …,parametroN
A la hora de llamar a la función, los parámetros se meterán directamente en la pila, de
derecha a izquierda (según el modelo stdcall), y se autocompensará. De este modo el equivalente
ensamblador será el siguiente, en la llamada anterior:
push parametroN
;[…]
push parametro2
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
42
push parametro1
call funcion
4. Directivas especiales para incluir ficheros y Modulación del código.
Con el ensamblador MASM32 se facilita la inclusión de librerías del sistema, con
numerosas APIs, que podrán usarse directamente. Para incluirlas se puede usar la directiva
includelib (en lugar del include para ficheros). Esta directiva hace que sólo se carguen aquellas
librerías de APIs que sean necesarias, y la optimización podría ampliarse hasta el punto de que
sólo se carguen las APIs que use nuestro programe, además del hecho de poder usarlas
cargadas en memoria como recurso común para múltiples procesos.
Por otro lado, a la hora de modular el código de nuestros programas, podemos usar la
directiva externdef, con la gran potencialidad de funcionar a la vez como el extern y global
conocidos de otros lenguajes de ensamblador. El compilador es capaz de reconocer
automáticamente si externdef debe traducirse por extern (si la variable no es declarada en el
fichero) o por global (si la variable sí es declarada en el fichero).
Finalmente, las funciones se exportando mostrando al exterior su prototipo, cuya forma de
declararlos es:
funcion PROTO :tipoParametro1, :tipoParametro2, …, :tipoParametroN
5. Secciones del programa.
Cuando se crean programas de ensamblador para entornos de Win32, tendremos las
siguientes posibilidades de secciones de código:
.const Æ Declaración de constantes
.data? Æ Contiene variables sin inicializar, lo cual es interesante, pues no es más que una
reserva para el momento de ejecución, por lo que nuestro fichero ejecutable no crecerá en
tamaño.
.data Æ Contiene variable inicializadas
.code Æ Contiene el código de programa
Finalmente, cuando el programa que se genere sea el principal, se deberá poner una
etiqueta de comienzo y la típica directiva end con la etiqueta de comienzo:
inicio:
; código
end inicio
Por otro lado, cuando el programa usa entorno gráfico hay que crear el manejador de la
ventana, la rutina que la crea, habitualmente llamada WinMain, y la rutina de control y
procesamiento de mensajes, habitualmente llamada WinProc. Esta última rutina es la que
implementa el código de las acciones para servir a los mensajes que nos mande Windows, tras
detectar que se han producido y son de nuestro programa (nuestra ventana activa).
6. Otras Directivas más comunes.
Finalmente, se puede hacer uso de útiles directivas como:
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
43
uses registro1 registro2 … registroN Æ Se encarga de salvar y restaurar automáticamente (con
instrucciones push y pop) los registros indicados. Se indica en la declaración de la interfaz de los
procedimientos (no en el prototipado).
addr Variable Æ Toma la dirección de la Variable. Su ventaja es que permite tomar direcciones
de variables locales (LOCAL Variable, que se declaran dentro de los procedimientos y que se
crean en tiempo de ejecución, almacenándose en la pila de ejecución del programa), lo cual se
traduce en el uso de la operación lea. Pero además funcionará incluso si no es una variable
local, sino global. Sin embargo, no pueden usarse en operaciones con registros, pues
habitualmente se emplean para el paso de parámetros. En caso de querer tomar direcciones con
instrucciones de registros podemos usar lea directamente, o bien, si son variables globales,
podemos emplear la directiva offset, que si recordamos que estamos en el modelo plano (flat)
tomará toda la dirección de 32 bits, pues no hay segmentos, ya que todo el espacio de memoria
es visible.
3.7. Interrelación entre Programas
A continuación se muestran sendos diagramas en los que se puede ver claramente la
interrelación que existe entre los distintos módulos de programas desarrolladas, tanto para la
parte del PC, como para la parte del PIC, tratadas seguidamente por separado.
3.7.1. Programa del PC
En el caso del PC, el programa usado para crear los códigos y compilarlos es el
RadAsm, a la vez que se usa el OllyDbg para la depuración, de los que se tiene más
información en la bibliografía. Al hacer programas gráficos se generan numerosos
ficheros que no son de especial interés en el desarrollo, si bien son necesarios para el
correcto funcionamiento del programa.
En el siguiente diagrama se muestran los programas con código significativo que
se han desarrollado, es decir, aquéllos en los que se han implementados aspectos
importantes del programa desarrollado.
3.7.2. Programa del PIC
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
44
En el caso del PIC, se ha usado el conocido compilador y depurador (entorno IDE
completo) denominado MPLAB. En este caso se muestra un diagrama con los ficheros
desarrollados en ensamblador PIC, así como las librerías creadas. Hay que indicar, que
aunque no se muestre en el diagrama, existirán los correspondientes ficheros fuentes para
las librerías creadas. De este modo, el diagrama resultante es el siguiente.
3.8. Ejecución emulada en Proteus® de la Placa PIC-Trainer
Los programas desarrollados para el PIC pueden ser emulados con ayuda del emulador
de circuitos electrónicos, llamado Proteus®. En este emulador, el primer paso será construir un
diseño similar al de la placa PIC-Trainer y PIC-Trainer Plus de la que se dispone físicamente en los
laboratorios. A continuación puede verse el diseño equivalente realizado, cuya construcción se
fundamenta en la teoría electrónica y los propios esquemáticos de las placas del laboratorio, que
se puede consultar en la memoria de la Práctica 1.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
45
En el diseño se observa que está un PIC16F84 que se conecta con conmutadores para
emular el puerto A y el puerto B. A su vez, ambos disponen de displays de 7 segmentos, con la
finalidad de ver el estado de ambos puertos. En el caso del puerto A se observa que la línea de
PC_LISTO viene indicada, pues equivaldrá a la señal que viene del PC, la cual usaremos como
control. Por otro lado, la línea de PIC_LISTO, no tiene conmutador de entrada, pues es la que se
usa para el control por parte del PIC hacia el PC.
Por otro lado, se pueden ver un LCD virtual, con un funcionamiento idéntico al de las
placas del laboratorio, con la única diferencia de que funciona pasivamente, pues no se
despierta de los bits BUSY en la rutina LCD_INI. Igualmente tenemos un teclado hexadecimal que
devuelve los mismos valores que el de la placa PIC-Trainer Plus; su único problema es la
orientación, pero para darle más comprensibilidad, se muestran los números y letras, de valores
hexadecimales, para que sea equivalente al de las placas del laboratorio.
El PIC16F84 de la placa emulada tendrá cargado el mismo programa que cargaremos en
la placa real. De este modo, el control del PC lo haremos manualmente con la línea del puerto A
indicada a tal efecto. Así, los pasos a seguir serán tan sencillos como poner el código de un
comando y usar el bit de PC_LISTO para que se realice la recepción de dicho comando a
emular. Con ello el programa bifurca a la zona que lo tratará. Conociendo su código podremos
saber que datos recibe o envía el PIC al PC, para ese comando, y de ese modo podremos ver
claramente como funciona todo perfectamente.
Como ejemplo sencillo se puede usar el comando de escritura de datos en el LCD
(comando 9, que en binario es 1001). Una vez se bifurque al comando bastará emular envíos
del PC al PIC de cada dato a escribir en el LCD, como puede ser el “0” (en hexadecimal 0x30).
Para terminar el subprograma de emulación del comando de escribir datos en el LCD, bastará
mandar un valor 0 en binario, en cuyo caso se volverá a esperar por un comando nuevo. En la
siguiente captura puede verse el estado del LCD tras recibir el “0”:
Así, se podrán seguir escribiendo más caracteres en el LCD, y cuando se mande un 0
binario (equivalente a la terminación de ristra), se terminará la transmisión de datos al LCD, el
cual mantendrá los datos recibidos.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
46
En este caso se muestra sólo una sección de la placa emulada, y es que hay que
entenderla como una placa que consta de varias secciones.
Para terminar falta comentar como se ha realizado la emulación del bus I2C. Aunque se
dispone de elementos con protocolo I2C en Proteus®, su funcionamiento parece ser pasivo y no
se ajusta a nuestras necesidades. Por ello, se emula poniendo dos conmutadores que funcionan
en modo pull-up. Para su uso es necesario usarlos en modo depuración, para mandar las señales
oportunas en el momento adecuado según las especificaciones de respuesta de los elementos
esclavos conectados al bus, durante una operación de envío o recepción del bus I2C. Estos
conectores son los siguientes:
Presentes en la zona derecha de esta sección de la placa emulada:
Con esto se recogen todas las posibilidades de la placa real PIC-Trainer y PIC-Trainer
Plus, si bien, falta una semántica de uso para el bus I2C, al que se pueden conectar multitud de
elementos que realizarían numerosas tareas. Una posible mejor implementación emulada del bus
I2C en Proteus® podría realizarse con varias PIC, funcionando uno como maestro y el resto
como esclavos.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
47
4. Conclusiones
En este apartado se tratarán por separado las conclusiones y problemas que se han tenido en las
diferentes secciones en las que se divide el trabajo realizado, y que se muestran en los siguientes
apartado. Igualmente se muestran las posibles mejoras y ampliaciones que se pueden realizar sobre el
trabajo realizado.
4.1. Protocolo del Puerto Paralelo
En la realización del protocolo del puerto paralelo, se puede empezar mencionando el
hecho, en cuanto al aspecto hardware, que a la hora de conectar los cables del puerto paralelo
con el zócalo de conexión con la placa, la soldadura resulta una opción compleja. En su lugar se
optó por una solución menos ortodoxa pero eficiente, que consiste en el uso de las patillas de los
zócalos para conectarlas a los cables por su parte superior y por su parte inferior al zócalo.
Este sistema permite la facilidad de poder cambiar el conexionado con el zócalo, lo que
hace que tengamos un conexionado mucho más flexible. Sin embargo, es menos robusto,
aunque esto dependerá del mecanismo de conexión de los cables con la parte superior de las
patillas. Para ello existen tres posibilidades:
1. Pegamento Æ Puede provocar el aislamiento, y con ello la no conducción.
Además, suele ser poco robusto y tiende a soltarse.
2. Presión Æ Consiste simplemente en hacer presión en la unión, evitándose el
problema del aislamiento eléctrico, pero se puede perder en robustez.
3. Soldadura Æ Es un método más complejo, pero no excesivamente, y permite
tener la máxima flexibilidad y robustez conjuntamente.
En nuestro caso se optó por la presión, pero podría mejorarse, realizando la soldadura de
los cables necesarios.
Pasando a los aspectos del software, podríamos empezar por el diseño del algoritmo del
protocolo. Este algoritmo funciona bastante bien, pero hace uso de un bit del puerto paralelo que
hace uso de lógica negada, por lo que se complica enormemente su implementación y
comprensión. Por ello, una buena opción sería tomar una línea que no fuera de lógica negada.
En concreto necesitaríamos una línea de entrada salida, que por ello sería del registro de control
del puerto paralelo, para sustituir a la línea usada para PC_LISTO, que funciona con la línea de
lógica de ~STROBE, que se trata del bit 0 del registro de control.
Una posible mejora, pero que en realidad no es necesaria, podría ser la realización de un
cambio en el estado de reposo (el fijado en la inicialización). Este cambia tendría por objeto
mantener el registro de datos del PC en modo entrada (en realidad sería entrada/salida),
mientras que en el PIC sería de salida en PORTB. Esto permitiría ver en todo momento el valor de
PORTB. Sin embargo, no es algo absolutamente necesario, pues para ello se tiene el programa
gráfico del PC, que en caso de leer el valor del puerto B del PIC, devolverá el valor que éste
tiene, aunque no sea visible.
Una de las mejoras más recomendables, pero de tremenda complejidad, sería hacer que
el protocolo no use espera activa, haciendo uso de las interrupciones del puerto paralelo. Para
ello habría que habilitarlas y hacer un uso correcto de las mismas. Pero igualmente se tendría
que diseñar el programa del PC con tareas, para ganar en eficiencia en el mismo y evitar un
consumo excesivo de CPU. Esto también podría llevarse a aplicarlo al PIC, durmiéndolo mientras
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
48
no se reciba nada y usando la señal de PC_LISTO para poder despertar al PIC y que termine
recibiendo el dato.
Finalmente, se puede señalar que se observa que el protocolo usado es bastante rápido y
permite evitar el uso de retardos, que tal vez podrían usarse para garantizar un mejor
funcionamiento, pero que en principio, y gracias a las dos señales de control se puede evitar. De
este modo se tiene un protocolo de transmisión rápido y con un buen ancho de bus, atendiendo
a la tarea y medios que estamos usando.
4.2. Intérprete Gráfico del PC (Emulador)
En el intérprete gráfico realizado, se tiene una buena interfaz gráfica que permite una fácil
creación de programas emulados, así como una sencilla compilación y uso. Siempre estará
abierto el programa a una ampliación en el set de instrucciones, la cual partiría del hecho de
añadir más instrucciones o comandos, con sus respectivos parámetros de entrada y salida, en el
fichero de sintaxis (reglasComp.txt).
Una mejora posible, aunque no trivial sería la de facilitar que el proceso de añadir
instrucciones se haga simplemente en el fichero de sintaxis, sin requerir tocar código del
programa. Esto debe tratarse con especial cuidado para evitar que se desborde la pila del
programa y nuestro programa falle.
Por otro lado, podría añadirse opciones mejoradas, como la opción de un depurador con
ejecución paso a paso, pero que en principio requieren bastante tiempo, tanto el dedicado a la
interfaz gráfica, como el dedicado a la programación, si bien ya tiene toda la base
implementada.
Por último, y como ya se mencionó anteriormente, una buena mejora para dotar de
eficiencia al programa, consiste en hacer que cada función funcione como una tarea aparte. De
hecho, en el caso de uso de un depurador, el uso de tareas y control de concurrencia será
absolutamente necesaria.
4.3. Ejecución de Instrucciones Emuladas en el PIC
En el programa del PIC se partía del código desarrollado en años previos. En esta
ocasión se ha mejorado y adaptado al protocolo del puerto paralelo. El nuevo código controla
con cuidado los cambios de páginas, según el valor de PCLATH. Una vez controlado esto, se
pueden realizar los saltos relativos a PCL sin problemas. Con esto, el tamaño de nuestro
programa se reduce exponencialmente. Además, el programa final tiene el código reducido al
máximo, tanto para obtener menos ocupación en el PIC, como conseguir mayor velocidad de
ejecución.
Finalmente, hay que añadir que el programa puede requerir el uso de ciertos retardos en
algunas zonas, para lo cual se dispone de una librería de retardos bastante completa y explicada
a lo largo de la memoria. Con ella se pueden poner retardos en aquellas zonas críticas del
programa del PIC.
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
49
5. Bibliografía
Programas:
Microsoft Word (Realización de la Memoria)
MS Visio (Diseño de diagramas de flujo)
EditPlus (Visualización de código ensamblador, con sintaxis de ensamblador PIC)
MPLAB (IDE para la programación en ensamblador PIC y su depuración)
Proteus (Diseño de circuito de la placa PIC Trainer y ejecución simulado de programas)
Cargador de Programas en PIC (de Microchip®)
Internet Explorer (Búsqueda de documentación e información)
Acrobat Reader (Visualización de documentación)
CallDelay (Programa de generación de rutinas de retardo)
MASM32 (Compilador de Microsoft Macro Assambler para Win32)
RadASM (Entorno IDE para programación en Win32, usando en este caso MASM32)
OllyDbg (Depurador Externo para la depuración de programas Win32)
Fuentes Teóricas:
Ayuda de MPLAB (Uso de MPLAB y consulta del ensamblador PIC)
Ayuda de MASM32 (Conocimiento del ensamblador MASM32)
Ayuda de las API de Windows® (Conocimiento de la Interfaz de las APIs)
Ayuda de RadASM (Uso de RadASM y diseño de programas gráficos)
Manual de Ensamblador en Win32 (Programación en ensamblador para Win32)
Documentación de la asignatura DSBM
Manual de Microcontrolador PIC (Información del PIC + Ensamblador PIC)
http://x-robotics.com (Información de Microcontroladores y Simuladores)
Páginas Webs Interesantes
http://www.movsd.com Æ Web Oficial del MASM32
http://radasm.visualassembler.com Æ Ensamblador RadAsm para MASM, TASM,...
http://board.win32asmcommunity.net Æ Foros de ensamblador en Win32
http://home.t-online.de/home/Ollydbg/ Æ Depurardor OllyDbg, para programas de
entorno gráfico Win32
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
50
6. Anexo
En esta sección nos limitaremos a presentar los códigos desarrollados, en algunos casos
acompañados de una breve explicación de la tarea que realizan. Cada código se presenta según el
fichero en que estaba contenido, lo que permite observar la modulación que se ha adoptado en los
programas desarrollados.
6.1. Códigos del PIC
Los códigos del PIC, desarrollados bajo MPLAB®, son los siguientes, creados en ficheros
.asm, pero que en ocasiones se han convertido a librerías (.lib) para su mejor manejo.
6.1.1. Emulador de Comandos (main.asm)
Es el programa principal del PIC, que recibe el comando y bifurca
inmediatamente al punto del programa que lo procesará. El procesamiento de un
comando podrá implicar otras transmisiones con el PC, que pueden ser tanto de envío
como recepción. Todo ello puede verse en el siguiente código, que aunque es el que se
usa en la práctica, también está abierto a ciertas modificaciones ya explicadas en su
momento.
#include "Lib\InstrPIC16F84.h"
#include "Lib\i2c.inc"
#include "Lib\lcd.inc"
#include "Lib\paralelo.inc"
#include "Lib\retardo.inc"
#include "Lib\tecla.inc"
radix dec ; Números decimales por defecto
origen code 0
goto LeerComando
main code
LeerComando
call Inicializar
call Recibir
; Recibir Comando
movfw DatoPP
addwf PCL
goto leerA
; 0 --> leer del puerto A
goto leerB
; 1 --> leer del puerto B
goto escribirA
; 2 --> escribir en puerto A
goto escribirB
; 3 --> escribir en puerto B
goto leerteclado
; 4 --> leer teclado hexadecimal
goto escribir_1_byte ; 5 --> escribir un solo byte en bus I2C
goto leer_1_byte
; 6 --> leer un solo byte del bus I2C
goto escribir_n_bytes ; 7 --> escribir "n" bytes en bus I2C
goto leer_n_bytes
; 8 --> leer "n" bytes del bus I2C
goto env_dato_lcd ; 9 --> enviar dato al LCD
goto env_com_lcd ; 10 --> enviar comando al LCD
goto terror
; ERROR --> si no es ningún comando
leerA
Mod_Tris Ent,PORTA
movar DatoPP,PORTA
call Inicializar
; Restaura el modo de PORTA
call Enviar ; Envía el Dato (de PORTA), los valores de PORTA[3..4] son espúreos, por usarse en el protocolo
goto LeerComando
leerB
Mod_Tris Sal,PORTB
movar PORTB,DatoPORTB
; Mod_Tris Ent,PORTB
movar DatoPP,PORTB
; Si PC estuviera como entrada
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
51
call Inicializar
call Enviar
goto LeerComando
escribirA ; Este comando no es visible en la placa PIC-Trainer!!!
call Recibir
Mod_Tris Sal,PORTA
movar PORTA,DatoPP
goto LeerComando
escribirB
call Recibir
Mod_Tris Sal,PORTB
movar PORTB,DatoPP
;Mod_Tris Ent,PORTB
movar DatoPORTB,PORTB
goto LeerComando
; Si PC estuviera como entrada
leerteclado
call Key_Scan
; Se realiza barrido del teclado
movlw 0x80
subwf Tecla,W
; Se ha pulsado alguna tecla???
btfsc STATUS,Z
goto leerteclado
; Se sigue explorando el teclado
; Se salva PCLATH (DatoPP se usa como variable auxiliar), pues Cods_Tecla lo modifica, y se restaura
movar DatoPP,PCLATH
movfw Tecla
call Cods_Tecla
movar PCLATH,DatoPP
movar DatoPP,POS ; Valor hexadecimal de la tecla
call Inicializar
call Enviar
goto LeerComando
escribir_1_byte
call Recibir
movar DATO,DatoPP
call Recibir
movar DIRECCION,DatoPP
clrf BUS_STATE
; Resetea registro estado del bus I2C por si tiene basura
call ENVIAR_1
; Rutina de escritura de 1 byte en el bus
goto LeerComando
leer_1_byte
call Recibir
movar DIRECCION,DatoPP
clrf BUS_STATE
call RECIBIR_1
; Resetea registro estado del bus I2C por si tiene basura
; Rutina de lectura de 1 byte en el bus
movar DatoPP,DATO
call Inicializar
call Enviar
goto LeerComando
escribir_n_bytes
call Recibir
movar CONT_BYTE, DatoPP
call Recibir
movar DIRECCION,DatoPP
bucle1
call Inicializar
call Recibir
movar DATO,DatoPP
clrf BUS_STATE
call ENVIAR
movfw CONT_BYTE
btfss STATUS,Z
; Quedan bytes por recibir?
goto bucle1
goto LeerComando
leer_n_bytes
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
52
call Recibir
movar CONT_BYTE, DatoPP
call Recibir
movar DIRECCION,DatoPP
bucle2
;call RetardoMS ; Retardo entre byte y byte
clrf BUS_STATE
call RECIBIR
movar DatoPP,DATO
call Inicializar
call Enviar
movfw CONT_BYTE
btfss STATUS,Z
; Quedan bytes por recibir?
goto bucle2
goto LeerComando
env_dato_lcd
call Inicializar
; Prepara puertos para poder usar protocolo
call Recibir
call DISPLAY_CONF ; Prepara puertos y LCD para ser usados
movfw DatoPP
btfsc STATUS,Z
; Si el dato recibido es 0 --> No lo envía y termina el comando
goto LeerComando
movfw DatoPP
; Necesario para que el LCD no se quede BUSY!!!
call LCD_DATO
;call RetardoMS
; Retardo en función de la velocidad del LCD
goto env_dato_lcd
env_com_lcd
call Recibir
movfw DatoPP
call LCD_REG
goto LeerComando
terror
BANK1
clrf TRISB ; Equivale a Mod_Tris Sal,PORTB
BANK0
movar PORTB,DatoPORTB
goto $
; Se detiene el PIC en este punto
end
6.1.2. Protocolo del Puerto Paralelo (paralelo.asm)
Contiene las rutinas de Inicializar, Enviar y Recibir para el PIC, a parte de las
variables DatoPP para envío y recepción, y DatoPORTB para mantener el dato del puerto
B.
#include "Lib\InstrPIC16F84.h"
global Inicializar, Enviar, Recibir, DatoPP, DatoPORTB
; Posiciones de los bits de control (en PORTA)
PIC_LISTO
equ 3
PC_LISTO
equ 4
d_prot
udata
DatoPP
DatoPORTB
res 1
res 1
c_prot code
Inicializar
; Se inicializan los puertos de la siguiente forma:
; PORTB --> Entrada; Contiene DatoPORTB (sería un parámetro de entrada)
; PORTA --> Salida, con PC_LISTO como Entrada; Se mantiene a 0
BANK1
movlw Ent
movwf TRISB
; PORTB como entrada
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
53
clrf TRISA
bsf TRISA,PC_LISTO ; PORTA como salida, y PC_LISTO como entrada
bsf OPTION_REG,NOT_RBPU ; Desactivar cargas pull-up
BANK0
clrf PORTA
; Limpia PORTA
movfw DatoPORTB PORTB = DatoPORTB
movwf PORTB
return
Recibir
; PASO 1: Poner PIC en Estado de Reposo (poner PIC_LISTO a 0)
bcf PORTA,PIC_LISTO
; PASO 2: Esperar a que el PC haya puesto el Dato (listo para transmitir)
btfss PORTA,PC_LISTO
; Si PORTA(PC_LISTO) == 1 ==> Pasa a PASO 4
goto $-1
; PASO 3: Tomar el Dato
movar DatoPP,PORTB
; PASO 4: PIC indica que leyo el Dato (poner PIC_LISTO a 1)
bsf PORTA,PIC_LISTO
; PASO 5: Esperar a que PC se diera cuenta de que se leyo el Dato
;
(se termino la transmision)
btfsc PORTA,PC_LISTO
; Si PORTA(PC_LISTO) == 0 ==> Pasa a PASO 7
goto $-1
; PASO 6: Se restaura el valor de PORTB
movar PORTB,DatoPORTB
; PASO 7: Se pasa a Estado de Reposo (poner PIC_LISTO a 0)
bcf PORTA,PIC_LISTO
return
Enviar
; PASO 1: Se comprueba que el PC quiere iniciar una recepcion (PC_LISTO == 0) por la logica negada
btfsc PORTA,PC_LISTO
; Si PORTA(PC_LISTO) == 0 ==> Pasa a PASO 2
goto $-1
; PASO 2: Poner PORTB como Salida
BANK1
clrf TRISB
BANK0
; PASO 3: Poner el Dato en PORTB
movar PORTB,DatoPP
; PASO 4: Indicar que el Dato esta listo para enviarse (PIC_LISTO == 1)
bsf PORTA,PIC_LISTO
; PASO 5: Esperar a que el PC haya recibido el Dato (PC_LISTO == 1) por la logica negada
btfss PORTA,PC_LISTO
; Si PORTA(PC_LISTO) == 1 ==> Pasa a PASO 7
goto $-1
; PASO 6: Se pone PORTB como Entrada y se restaura el valor de PORTB
Mod_Tris Ent,PORTB
movar PORTB,DatoPORTB
; PASO 7: Se pasa a Estado de Reposo (poner PIC_LISTO a 0)
bcf PORTA,PIC_LISTO
return
end
Para facilitar el uso de las rutinas del protocolo del puerto paralelo desarrolladas,
se ha creado una librería para las mismas, bajo el nombre paralelo.lib. Su creación es
bastante sencilla, realizable a partir del fichero objeto (paralelo.o) que se obtiene a partir
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
54
de la compilación del fichero paralelo.asm. Bastará ejecutar el siguiente comando de
consola con el programa mplib.exe:
mplib.exe /c paralelo.lib paralelo.o
6.1.3. Rutinas de Retardo (retardo.asm)
Las rutinas de retardo creadas son de propósito general, como ya se indicó, e
incluyen a todas éstas:
#include "Lib\InstrPIC16F84.h"
radix dec ; Números decimales, por defecto
global Retardo1, Retardo2, Retardo3, RetardoMS, RetardoCS, RetardoDS, RetardoS, N1, N2, N3
d_retardo udata
N1 res 1
N2 res 1
N3 res 1
Naux1 res 1
Naux2 res 1
c_retardo code
;------------------------------------------------------------; Retardo de 6 a 768 microsegundos, usa 1 factor, por lo que
; soporta retardo de tiempos primos
;------------------------------------------------------------; Duración = N1*3 - 1 + 4
; Duración = 3*(N1+1) microsegundos, 0 < N1 <= 255
; Usar MatLab con solve('3*(N1+1) + 2 = X')
; Si se cuenta la asiganción moval N,microsegundos (+2)
;-----------------------------------------------------------Retardo1
decfsz N1 ; 1 + (1)
goto $-1 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 16 a 197628 microsegundos, usa 2 fatores, por lo
; que soporta retardo de tiempos factorizables a 2 valores
;------------------------------------------------------------; Duración = N2*( 3*(N1+1) + 7 ) - 1 + 4
; Duración = 3*N2*(N1+3) + N2 + 3 microsegundos,
; 0 < N1,N2 <= 255
; Usar MatLab con solve('3*N2*(N1+3) + N2 + 3 + 4 = X')
; Si se cuentan las asiganciones moval N1,microsegundos y
; moval N2,microsegundos (+4)
;------------------------------------------------------------Retardo2
movfw N1 ; 2
movwf Naux1
movfw Naux1 ; 2
movwf N1
call Retardo1 ; 3*(N1+1)
decfsz N2 ; 1 + (1)
goto $-4 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 26 a 50396928 microsegundos, usa 3 fatores, por
; lo que soporta retardo de tiempos factorizables a 3 valores
;------------------------------------------------------------; Duración = N3*( (3*N2*(N1+3) + N2 + 3) + 7 ) - 1 + 4
; Duración = 3*N3*N2*N1 + 10*N3*N2 + 10*N3 + 3
; Duración = 3*N3*N2*N1 + 10*N3*(N2+1) + 3 microsegundos,
; 0 < N1,N2,N3 <= 255
; Usar MatLab con solve('3*N3*N2*N1 + 10*N3*(N2+1) + 3 + 6 = X')
; Si se cuentan las asiganciones moval N1,microsegundos,
; moval N2,microsegundos y moval N3,microsegundos (+6)
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
55
;-----------------------------------------------------------Retardo3
movfw N2 ; 2
movwf Naux2
movfw Naux2 ; 2
movwf N2
call Retardo2 ; 3*N2*(N1+3) + N2 + 3
decfsz N3 ; 1 + (1)
goto $-4 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 1 milisegundo
;------------------------------------------------------------; Duración = 2 + 768 + 2 + 222 + 2 + 4 = 1000 microsegundos
;------------------------------------------------------------RetardoMS
movlw 255 ; 2
movwf N1
call Retardo1 ; 3*(255+1) = 768
movlw 74 ; 2
movwf N1
call Retardo1 ; 3*(73+1) = 222
goto $+1 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 1 centésima de segundo
;------------------------------------------------------------; Duración = 2 + 2 + 9961 + 2 + 27 + 2 + 4
; Duración = 10000 microsegundos
;------------------------------------------------------------RetardoCS
movlw 252 ; 2
movwf N1
movlw 13 ; 2
movwf N2
call Retardo2 ; 3*13*(252+3) + 13 + 3 = 9961
movlw 8 ; 2
movwf N1
call Retardo1 ; 3*(8+1) = 27
goto $+1 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 1 décima de segundo
;------------------------------------------------------------; Duración = 2 + 2 + 99973 + 2 + 15 + 2 + 4
; Duración = 100000 microsegundos
;------------------------------------------------------------RetardoDS
movlw 255 ; 2
movwf N1
movlw 130 ; 2
movwf N2
call Retardo2 ; 3*130*(253+3) + 130 + 3 = 99973
movlw 4 ; 2
movwf N1
call Retardo1 ; 3*(4+1) = 15
goto $+1 ; 2
return ; 2+2
;------------------------------------------------------------; Retardo de 1 segundo
;------------------------------------------------------------; Duración = 2 + 2 + 2 + 999813 + 2 + 174 + 1 + 4
; Duración = 1000000 microsegundos
;------------------------------------------------------------RetardoS
movlw 255 ; 2
movwf N1
movlw 215 ; 2
movwf N2
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
56
movlw 6 ; 2
movwf N3
call Retardo3 ; 3*6*215*255 + 10*6*(215+1) + 3 = 999813
movlw 57 ; 2
movwf N1
call Retardo1 ; 3*(57+1) = 174
nop ; 1
return ; 2+2
end
Para facilitar el uso de las rutinas de retardo, se ha creado una librería para las
mismas, bajo el nombre retardo.lib. Su creación es bastante sencilla, realizable a partir
del fichero objeto (retardo.o) que se obtiene a partir de la compilación del fichero
retardo.asm. Bastará ejecutar el siguiente comando de consola con el programa
mplib.exe:
mplib.exe /c retardo.lib retardo.o
En cuanto al uso de librerías hay que indicar que si bien sólo se cargan los
miembros que se usan, para que ocurra así debe evitarse la declaración extern. Por ello,
si se usa el fichero cabecera de la librería (ratardo.inc) se incluirá siempre todo. Además,
si se pretende que sólo se incluyan ciertas funciones, será necesario que se creen en
distintos miembros, es decir, distintos ficheros objeto, lo cual sería una tarea tedioso y
poco recomendable.
6.1.4. Fichero Cabecera (InstrPIC16F84.h)
Este fichero es una extensión del ya desarrollado en la Práctica 1. Incluye el
fichero de macros Standard del PIC16F84 más una serie de macros especiales para
desarrollo de aplicaciones en el PIC16F84. Su código es el siguiente:
processor PIC16F84
#include <p16f84.inc>
;---------------------------------------------------------; Inhibición de Mensajes y Avisos
;---------------------------------------------------------errorlevel -203 ; Operación en Columna 1
errorlevel -205 ; Directiva en Columna 1
errorlevel -206 ; Macro en Columna 1
errorlevel -207 ; Etiqueta No en Columna 1
errorlevel -216 ; Aviso de radix configurado en proyecto y código
errorlevel -302 ; Registro que no es de banco0 (asegurarse de que el banco sea el correcto)
errorlevel -305 ; Destino de operación por defecto (1 --> File)
;---------------------------------------------------------; Directivas de definición de Instrucciones fijas
; de uso común.
;---------------------------------------------------------Sal equ 0x00 ; Puerto de Salida
Ent equ 0xff ; Puerto de Entrada
; Ent funcionará igualmente para PORTA (aunque TRISA se pondrá a 0x1f)
;---------------------------------------------------------; Mod_Tris
;---------------------------------------------------------; Macro que estable el puerto de entrada y salida.
;---------------------------------------------------------Mod_Tris MACRO INOUT, PUERTO
BANK1
movlw INOUT
movwf PUERTO ; Equivale a TRIS, porque accedemos en banco 1
BANK0
ENDM
#define BANK0 bcf STATUS, RP0
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
57
#define BANK1 bsf STATUS, RP0
;---------------------------------------------------------; Macros para compactar instrucciones de determinadas
; tareas.
;---------------------------------------------------------; DetenerReloj --> Detiene Timer0
DetenerReloj MACRO
BANK1
bsf OPTION_REG,T0CS
BANK0
ENDM
; ReiniciarReloj --> Re-inicia Timer0
ReiniciarReloj MACRO
BANK1
bcf OPTION_REG,T0CS
BANK0
ENDM
;---------------------------------------------------------; Macros para definición de Pseudo-Instruccion más
; potentes, para la programación en lenguaje ensamblador.
;---------------------------------------------------------; moval a0, a1 --> a0 = a1 (a1 = literal) (modifica W)
moval MACRO a0, a1
movlw a1
movwf a0
ENDM
; movar a0, a1 --> a0 = a1 (a1 = registro) (modifica W)
movar MACRO a0, a1
movfw a1
movwf a0
ENDM
; addl a0, a1 --> a0 += a1 (a1 = literal) (modifica W)
addl MACRO a0, a1
movfw a0
addlw a1
movwf a0
ENDM
; addr a0, a1 --> a0 += a1 (a1 = registro) (modifica W)
addr MACRO a0, a1
movfw a1
addwf a0,F
ENDM
; subl a0, a1 --> a0 -= a1 (a1 = literal) (modifica W)
subl MACRO a0, a1
movlw a1
subwf a0,F
ENDM
; subr a0, a1 --> a0 -= a1 (a1 = registro) (modifica W)
subr MACRO a0, a1
movfw a1
subwf a0,F
ENDM
; xorl a0, a1 --> a0 += a1 (a1 = literal) (modifica W)
xorl MACRO a0, a1
movfw a0
xorlw a1
movwf a0
ENDM
; xorr a0, a1 --> a0 += a1 (a1 = registro) (modifica W)
xorr MACRO a0, a1
movfw a1
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
58
xorwf a0,F
ENDM
; eq a0, a1, etiqueta --> a0 == a1 ==> no salta a etiqueta (a1 = literal) (modifica W)
eq MACRO a0, a1, etiqueta
local iguales
subl a0,a1
skpnz
goto iguales
addl a0,a1
goto etiqueta
iguales
addl a0,a1
ENDM
; neq a0, a1, etiqueta --> a0 != a1 ==> no salta a etiqueta (a1 = literal) (modifica W)
neq MACRO a0, a1, etiqueta
local distintos
subl a0,a1
skpz
goto distintos
addl a0,a1
goto etiqueta
distintos
addl a0,a1
ENDM
; ge a0, a1, etiqueta --> a0 >= a1 ==> no salta a etiqueta (a1 = literal) (modifica W)
ge MACRO a0, a1, etiqueta
local no_mayor
subl a0,a1
bc no_mayor
addl a0,a1
goto etiqueta
no_mayor
addl a0,a1
ENDM
; pushi w_temp, status_temp --> Salva W y STATUS en memoria (w_temp = W; status_temp = STATUS)
pushi MACRO w_temp, status_temp
movwf w_temp
movar status_temp,STATUS
ENDM
; popi w_temp, status_temp --> Recupear W y STATUS de memoria (W = w_temp; STATUS = status_temp)
popi MACRO w_temp, status_temp
movar STATUS,status_temp
movfw w_temp
ENDM
6.2. Códigos del PC
A continuación se muestran sólo los ficheros principales del programa de entorno gráfico
desarrollado. En principio, al ser un programa de entorno gráfico, serán necesarios otros ficheros
de imágenes, contenidos especiales, etc. sin los que el programa no funcionará ni podrá ser
compilado, pero que no tiene código propiamente dicho, y que en ocasiones su generación es
automática con las herramientas de desarrollo usadas, en concreto el entorno IDE RadAsm®,
usando el ensamblador gráfico MASM32®, y el depurador de programas Win32 denominado
OllyDbg®.
6.2.1. Interfaz del Programa Gráfico Principal (Emulador.inc)
include windows.inc
include user32.inc
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
59
include kernel32.inc
include shell32.inc
include comctl32.inc
include comdlg32.inc
include gdi32.inc
include masm32.inc
includelib user32.lib
includelib kernel32.lib
includelib shell32.lib
includelib comctl32.lib
includelib comdlg32.lib
includelib gdi32.lib
includelib masm32.lib
;Modo Depuración (DEBUG == 1)
DEBUG = 1
WinMain PROTO :DWORD,:DWORD,:DWORD,:DWORD
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.const
IDB_TBRBMP equ 150
IDR_ACCEL equ 200
;Find.dlg
IDD_FINDDLG equ 2000
IDC_FINDTEXT equ 2001
IDC_BTN_REPLACE equ 2007
IDC_REPLACETEXT equ 2002
IDC_REPLACESTATIC equ 2009
IDC_BTN_REPLACEALL equ 2008
IDC_CHK_WHOLEWORD equ 2004
IDC_CHK_MATCHCASE equ 2003
IDC_RBN_DOWN equ 2005
IDC_RBN_UP equ 2006
;Emulador.dlg
IDD_DLG equ 1000
IDC_SBR equ 1003
IDC_TBR equ 1001
IDC_RED equ 1002
IDM_MENU equ 10000
;Emulador.mnu
IDM_FILE_NEW equ 10001
IDM_FILE_OPEN equ 10002
IDM_FILE_SAVE equ 10003
IDM_FILE_SAVEAS equ 10004
IDM_FILE_PRINT equ 10005
IDM_FILE_EXIT equ 10006
IDM_EDIT_UNDO equ 10101
IDM_EDIT_REDO equ 10102
IDM_EDIT_DELETE equ 10103
IDM_EDIT_CUT equ 10104
IDM_EDIT_COPY equ 10105
IDM_EDIT_PASTE equ 10106
IDM_EDIT_SELECTALL equ 10107
IDM_EDIT_FIND equ 10108
IDM_EDIT_FINDNEXT equ 10110
IDM_EDIT_FINDPREV equ 10111
IDM_EDIT_REPLACE equ 10109
IDM_VIEW_TOOLBAR equ 10008
IDM_VIEW_STATUSBAR equ 10009
IDM_OPTION_FONT equ 10007
IDM_HELP_ABOUT equ 10201
IDM_ENVIAR_DATO equ 10010
IDM_RECIBIR_DATO equ 10011
IDM_TEST equ 10012
IDM_COMPILAR equ 10013
IDM_COMPILAR_LINEA equ 10014
;structure for ToolBar buttons
tbrbtns TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
TBBUTTON <6,IDM_FILE_NEW,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <7,IDM_FILE_OPEN,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <8,IDM_FILE_SAVE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
60
TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
TBBUTTON <0,IDM_EDIT_CUT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <1,IDM_EDIT_COPY,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <2,IDM_EDIT_PASTE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
TBBUTTON <3,IDM_EDIT_UNDO,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <4,IDM_EDIT_REDO,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <5,IDM_EDIT_DELETE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
TBBUTTON <12,IDM_EDIT_FIND,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <13,IDM_EDIT_REPLACE,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
TBBUTTON <14,IDM_FILE_PRINT,TBSTATE_ENABLED,TBSTYLE_BUTTON,0,0>
TBBUTTON <0,0,TBSTATE_ENABLED,TBSTYLE_SEP,0,0>
;Number of buttons in tbrbtns
ntbrbtns equ 18
.data
RichEditDLL db 'riched20.dll',0
ClassName db 'DLGCLASS',0
AppName db 'El Emulador',0
AboutMsg db 'RadASM El Emulador',13,10,'Saldriamos Nosotros',0
Replace db 'Remplazar ..',0
OpenFileFail db 'No se puede abrir el fichero',0
SaveFileFail db 'No se puede guardar el fichero',0
WannaSave db 'Desea guardar los cambios realizados',0Dh,0
NewFile db '(Sin Título)',0
szNULL db 0
szFont db 'Courier New',0
msgDatoRecibido db 'Dato Recibido a través del Puerto Paralelo:',0
.data?
hRichEdDLL dd ?
hInstance dd ?
CommandLine dd ?
hIcon dd ?
hAccel dd ?
hWnd HWND ?
hREd HWND ?
hFind HWND ?
FileName db MAX_PATH dup(?)
fView dd ?
TabSize dd ?
lfnt LOGFONT <>
hFont dd ?
rgb dd ?
findbuff db 256 dup(?)
replacebuff db 256 dup(?)
ft FINDTEXTEX <>
fr dd ?
fres dd ?
6.2.2. Programa Gráfico Principal (Emulador.asm)
.386
.model flat,stdcall
option casemap:none
include Protocolo.inc
include Compilador.inc
include Emulador.Inc
include TraduceIns.inc
.data?
hMainHeap dd ?
MatrizDeSintaxis dd ?
.code
start:
invoke GetModuleHandle,NULL
mov hInstance,eax
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
61
invoke GetCommandLine
mov CommandLine,eax
invoke InitCommonControls
invoke LoadLibrary,offset RichEditDLL
mov hRichEdDLL,eax
;Creamos la pila del programa
invoke GetProcessHeap
mov hMainHeap,eax
invoke RellenaListaIns,hInstance,hMainHeap
push ecx
push edx
mov ecx, offset MatrizDeSintaxis
mov edx, dword ptr[eax]
mov dword ptr[ecx],edx
pop edx
pop ecx
invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT
push eax
invoke FreeLibrary,hRichEdDLL
pop eax
invoke ExitProcess,eax
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
mov wc.cbSize,sizeof WNDCLASSEX
mov wc.style,CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc,offset WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,DLGWINDOWEXTRA
push hInst
pop wc.hInstance
mov wc.hbrBackground,NULL
mov wc.lpszMenuName,IDM_MENU
mov wc.lpszClassName,offset ClassName
invoke LoadIcon,NULL,IDI_APPLICATION
mov hIcon,eax
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx,addr wc
invoke CreateDialogParam,hInstance,IDD_DLG,NULL,offset WndProc,NULL
mov hWnd,eax
invoke ShowWindow,hWnd,SW_SHOWNORMAL
invoke UpdateWindow,hWnd
invoke LoadAccelerators,hInstance,IDR_ACCEL
mov hAccel,eax
invoke Inicializar ; Inicialización del Puerto Paralelo y librería WinIo
.while TRUE
invoke GetMessage,addr msg,NULL,0,0
.break .if !eax
invoke IsDialogMessage,hFind,addr msg
.if !eax
invoke TranslateAccelerator,hWnd,hAccel,addr msg
.if !eax
invoke TranslateMessage,addr msg
invoke DispatchMessage,addr msg
.endif
.endif
.endw
mov eax,msg.wParam
ret
WinMain endp
StreamInProc proc hFile:DWORD,pBuffer:DWORD,NumBytes:DWORD,pBytesRead:DWORD
invoke ReadFile,hFile,pBuffer,NumBytes,pBytesRead,0
xor eax,1
ret
StreamInProc endp
StreamOutProc proc hFile:DWORD,pBuffer:DWORD,NumBytes:DWORD,pBytesWritten:DWORD
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
62
invoke WriteFile,hFile,pBuffer,NumBytes,pBytesWritten,0
xor eax,1
ret
StreamOutProc endp
SetWinCaption proc
LOCAL buffer[sizeof AppName+3+MAX_PATH]:BYTE
LOCAL buffer1[4]:BYTE
;Add filename to windows caption
invoke lstrcpy,addr buffer,offset AppName
mov eax,' - '
mov dword ptr buffer1,eax
invoke lstrcat,addr buffer,addr buffer1
invoke lstrcat,addr buffer,offset FileName
invoke SetWindowText,hWnd,addr buffer
ret
SetWinCaption endp
SaveFile proc lpFileName:DWORD
LOCAL hFile:DWORD
LOCAL editstream:EDITSTREAM
invoke
CreateFile,lpFileName,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
mov hFile,eax
;stream the text to the file
mov editstream.dwCookie,eax
mov editstream.pfnCallback,offset StreamOutProc
invoke SendMessage,hREd,EM_STREAMOUT,SF_TEXT,addr editstream
invoke CloseHandle,hFile
;Set the modify state to false
invoke SendMessage,hREd,EM_SETMODIFY,FALSE,0
mov eax,FALSE
.else
invoke MessageBox,hWnd,offset SaveFileFail,offset AppName,MB_OK
mov eax,TRUE
.endif
ret
SaveFile endp
SaveEditAs proc
LOCAL ofn:OPENFILENAME
LOCAL buffer[MAX_PATH]:BYTE
;Zero out the ofn struct
invoke RtlZeroMemory,addr ofn,sizeof ofn
;Setup the ofn struct
mov ofn.lStructSize,sizeof ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter,NULL
mov buffer[0],0
lea eax,buffer
mov ofn.lpstrFile,eax
mov ofn.nMaxFile,sizeof buffer
mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST or
OFN_OVERWRITEPROMPT
mov ofn.lpstrDefExt,NULL
;Show save as dialog
invoke GetSaveFileName,addr ofn
.if eax
invoke SaveFile,addr buffer
.if !eax
;The file was saved
invoke lstrcpy,offset FileName,addr buffer
invoke SetWinCaption
mov eax,FALSE
.endif
.else
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
63
.endif
ret
SaveEditAs endp
mov eax,TRUE
SaveEdit proc
;Check if filrname is (Untitled)
invoke lstrcmp,offset FileName,addr NewFile
.if eax
invoke SaveFile,offset FileName
.else
invoke SaveEditAs
.endif
ret
SaveEdit endp
WantToSave proc
LOCAL buffer[512]:BYTE
LOCAL buffer1[2]:BYTE
invoke SendMessage,hREd,EM_GETMODIFY,0,0
.if eax
invoke lstrcpy,addr buffer,offset WannaSave
invoke lstrcat,addr buffer,offset FileName
mov ax,'?'
mov word ptr buffer1,ax
invoke lstrcat,addr buffer,addr buffer1
invoke MessageBox,hWnd,addr buffer,offset AppName,MB_YESNOCANCEL or MB_ICONQUESTION
.if eax==IDYES
invoke SaveEdit
.elseif eax==IDNO
mov eax,FALSE
.else
mov eax,TRUE
.endif
.endif
ret
WantToSave endp
LoadFile proc lpFileName:DWORD
LOCAL hFile:DWORD
LOCAL editstream:EDITSTREAM
LOCAL chrg:CHARRANGE
;Open the file
invoke
CreateFile,lpFileName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
.if eax!=INVALID_HANDLE_VALUE
mov hFile,eax
;Copy buffer to FileName
invoke lstrcpy,offset FileName,lpFileName
;stream the text into the richedit control
push hFile
pop editstream.dwCookie
mov editstream.pfnCallback,offset StreamInProc
invoke SendMessage,hREd,EM_STREAMIN,SF_TEXT,addr editstream
invoke CloseHandle,hFile
invoke SendMessage,hREd,EM_SETMODIFY,FALSE,0
mov chrg.cpMin,0
mov chrg.cpMax,0
invoke SendMessage,hREd,EM_EXSETSEL,0,addr chrg
invoke SetWinCaption
mov eax,FALSE
.else
invoke MessageBox,hWnd,offset OpenFileFail,offset AppName,MB_OK or MB_ICONERROR
mov eax,TRUE
.endif
ret
LoadFile endp
OpenEdit proc
LOCAL ofn:OPENFILENAME
LOCAL buffer[MAX_PATH]:BYTE
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
64
;Zero out the ofn struct
invoke RtlZeroMemory,addr ofn,sizeof ofn
;Setup the ofn struct
mov ofn.lStructSize,sizeof ofn
push hWnd
pop ofn.hwndOwner
push hInstance
pop ofn.hInstance
mov ofn.lpstrFilter,NULL
mov buffer[0],0
lea eax,buffer
mov ofn.lpstrFile,eax
mov ofn.nMaxFile,sizeof buffer
mov ofn.lpstrDefExt,NULL
mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_PATHMUSTEXIST
;Show the Open dialog
invoke GetOpenFileName,addr ofn
.if eax
invoke LoadFile,addr buffer
.endif
ret
OpenEdit endp
Find proc frType:DWORD
;Get current selection
invoke SendMessage,hREd,EM_EXGETSEL,0,offset ft.chrg
;Setup find
mov eax,frType
and eax,FR_DOWN
.if eax
.if fres!=-1
inc ft.chrg.cpMin
.endif
mov ft.chrg.cpMax,-1
.else
mov ft.chrg.cpMax,0
.endif
mov ft.lpstrText,offset findbuff
;Do the find
invoke SendMessage,hREd,EM_FINDTEXTEX,frType,offset ft
mov fres,eax
.if eax!=-1
;Mark the foud text
invoke SendMessage,hREd,EM_EXSETSEL,0,offset ft.chrgText
.else
;Region searched
.endif
ret
Find endp
FindDlgProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL hCtl:DWORD
mov eax,uMsg
.if eax==WM_INITDIALOG
mov eax,hWin
mov hFind,eax
.if lParam
mov eax,BN_CLICKED
shl eax,16
or eax,IDC_BTN_REPLACE
invoke PostMessage,hWin,WM_COMMAND,eax,0
.endif
;Put text in edit boxes
invoke SendDlgItemMessage,hWin,IDC_FINDTEXT,EM_LIMITTEXT,255,0
invoke SendDlgItemMessage,hWin,IDC_FINDTEXT,WM_SETTEXT,0,offset findbuff
invoke SendDlgItemMessage,hWin,IDC_REPLACETEXT,EM_LIMITTEXT,255,0
invoke SendDlgItemMessage,hWin,IDC_REPLACETEXT,WM_SETTEXT,0,offset replacebuff
;Set check boxes
mov eax,fr
and eax,FR_MATCHCASE
.if eax
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
65
invoke CheckDlgButton,hWin,IDC_CHK_MATCHCASE,BST_CHECKED
.endif
mov eax,fr
and eax,FR_WHOLEWORD
.if eax
invoke CheckDlgButton,hWin,IDC_CHK_WHOLEWORD,BST_CHECKED
.endif
;Set find direction
mov eax,fr
and eax,FR_DOWN
.if eax
mov eax,IDC_RBN_DOWN
.else
mov eax,IDC_RBN_UP
.endif
invoke CheckDlgButton,hWin,eax,BST_CHECKED
.elseif eax==WM_COMMAND
mov eax,wParam
mov edx,eax
shr edx,16
and eax,0FFFFh
.if edx==BN_CLICKED
.if eax==IDOK
;Find the text
invoke Find,fr
.elseif eax==IDCANCEL
;Close the find dialog
invoke SendMessage,hWin,WM_CLOSE,NULL,NULL
.elseif eax==IDC_BTN_REPLACE
invoke GetDlgItem,hWin,IDC_BTN_REPLACEALL
mov hCtl,eax
invoke IsWindowEnabled,hCtl
.if !eax
;Enable Replace all button
invoke EnableWindow,hCtl,TRUE
;Set caption to Replace...
invoke SetWindowText,hWin,offset Replace
;Show replace
invoke GetDlgItem,hWin,IDC_REPLACESTATIC
invoke ShowWindow,eax,SW_SHOWNA
invoke GetDlgItem,hWin,IDC_REPLACETEXT
invoke ShowWindow,eax,SW_SHOWNA
.else
.if fres!=-1
invoke SendMessage,hREd,EM_EXGETSEL,0,offset ft.chrg
invoke SendMessage,hREd,EM_REPLACESEL,TRUE,offset
replacebuff
invoke lstrlen,offset replacebuff
add eax,ft.chrg.cpMin
mov ft.chrg.cpMax,eax
invoke SendMessage,hREd,EM_EXSETSEL,0,offset ft.chrg
.endif
invoke Find,fr
.endif
.elseif eax==IDC_BTN_REPLACEALL
.if fres==-1
invoke Find,fr
.endif
.while fres!=-1
mov eax,BN_CLICKED
shl eax,16
or eax,IDC_BTN_REPLACE
invoke SendMessage,hWin,WM_COMMAND,eax,0
.endw
.elseif eax==IDC_RBN_DOWN
;Set find direction to down
or fr,FR_DOWN
mov fres,-1
.elseif eax==IDC_RBN_UP
;Set find direction to up
and fr,-1 xor FR_DOWN
mov fres,-1
.elseif eax==IDC_CHK_MATCHCASE
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
66
;Set match case mode
invoke IsDlgButtonChecked,hWin,IDC_CHK_MATCHCASE
.if eax
or fr,FR_MATCHCASE
.else
and fr,-1 xor FR_MATCHCASE
.endif
mov fres,-1
.elseif eax==IDC_CHK_WHOLEWORD
;Set whole word mode
invoke IsDlgButtonChecked,hWin,IDC_CHK_WHOLEWORD
.if eax
or fr,FR_WHOLEWORD
.else
and fr,-1 xor FR_WHOLEWORD
.endif
mov fres,-1
.endif
.elseif edx==EN_CHANGE
;Update text buffers
.if eax==IDC_FINDTEXT
invoke SendDlgItemMessage,hWin,eax,WM_GETTEXT,sizeof findbuff,offset
findbuff
mov fres,-1
.elseif eax==IDC_REPLACETEXT
invoke SendDlgItemMessage,hWin,eax,WM_GETTEXT,sizeof replacebuff,offset
replacebuff
mov fres,-1
.endif
.endif
.elseif eax==WM_ACTIVATE
mov fres,-1
.elseif eax==WM_CLOSE
mov hFind,0
invoke DestroyWindow,hWin
invoke SetFocus,hREd
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
FindDlgProc endp
DoToolBar proc hInst:DWORD,hToolBar:HWND
LOCAL tbab:TBADDBITMAP
;Set toolbar struct size
invoke SendMessage,hToolBar,TB_BUTTONSTRUCTSIZE,sizeof TBBUTTON,0
;Set toolbar bitmap
push hInst
pop tbab.hInst
mov tbab.nID,IDB_TBRBMP
invoke SendMessage,hToolBar,TB_ADDBITMAP,15,addr tbab
;Set toolbar buttons
invoke SendMessage,hToolBar,TB_ADDBUTTONS,ntbrbtns,offset tbrbtns
mov eax,hToolBar
ret
DoToolBar endp
SetFormat proc hWin:DWORD
LOCAL chrg1:CHARRANGE
LOCAL chrg2:CHARRANGE
LOCAL pf:PARAFORMAT2
LOCAL cf:CHARFORMAT
LOCAL tp:DWORD
LOCAL buffer[16]:BYTE
LOCAL pt:POINT
LOCAL hDC:HDC
;Save modify state
invoke SendMessage,hWin,EM_GETMODIFY,0,0
push eax
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
67
;Save selection
invoke SendMessage,hWin,EM_EXGETSEL,0,addr chrg1
invoke SendMessage,hWin,EM_HIDESELECTION,TRUE,0
;Select all text
mov chrg2.cpMin,0
mov chrg2.cpMax,-1
invoke SendMessage,hWin,EM_EXSETSEL,0,addr chrg2
;Set font charset
mov cf.cbSize,sizeof cf
mov cf.dwMask,CFM_CHARSET or CFM_FACE or CFM_SIZE or CFM_COLOR
mov al,lfnt.lfCharSet
mov cf.bCharSet,al
mov al,lfnt.lfPitchAndFamily
mov cf.bPitchAndFamily,al
invoke lstrcpyn,addr cf.szFaceName,offset lfnt.lfFaceName,LF_FACESIZE
mov eax,lfnt.lfHeight
neg eax
mov ecx,15
mul ecx
mov cf.yHeight,eax
mov eax,rgb
mov cf.crTextColor,eax
invoke SendMessage,hWin,EM_SETCHARFORMAT,SCF_SELECTION,addr cf
;Get tab width
invoke GetDC,hWin
mov hDC,eax
invoke SelectObject,hDC,hFont
push eax
mov eax,'WWWW'
mov dword ptr buffer,eax
invoke GetTextExtentPoint32,hDC,addr buffer,4,addr pt
pop eax
invoke SelectObject,hDC,eax
invoke ReleaseDC,hWin,hDC
mov eax,pt.x
mov ecx,TabSize
mul ecx
mov ecx,15
mul ecx
shr eax,2
mov tp,eax
;Set tab stops
mov pf.cbSize,sizeof pf
mov pf.dwMask,PFM_TABSTOPS
mov pf.cTabCount,MAX_TAB_STOPS
xor eax,eax
xor edx,edx
mov ecx,MAX_TAB_STOPS
@@:
add eax,tp
mov dword ptr pf.rgxTabs[edx],eax
add edx,4
loop @b
invoke SendMessage,hWin,EM_SETPARAFORMAT,0,addr pf
;Restore modify state
pop eax
invoke SendMessage,hWin,EM_SETMODIFY,eax,0
;Restore selection
invoke SendMessage,hWin,EM_EXSETSEL,0,addr chrg1
invoke SendMessage,hWin,EM_HIDESELECTION,FALSE,0
ret
SetFormat endp
WndProc proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL pt:POINT
LOCAL rect:RECT
LOCAL ht:DWORD
LOCAL hCtl:HWND
LOCAL chrg:CHARRANGE
LOCAL cf:CHOOSEFONT
LOCAL buffer[256]:BYTE
LOCAL Dato:BYTE ; Dato de Envío/Recepción
IF DEBUG
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
68
LOCAL Num:DWORD ; Para conversión de Entero a Ristra
LOCAL Ristra:DWORD
ENDIF
LOCAL RDato[8]:BYTE ; Ristra de Bytes que componen el Dato, para su muestreo
mov eax,uMsg
.if eax==WM_INITDIALOG
push hWin
pop hWnd
mov fr,FR_DOWN
mov fView,3
mov TabSize,4
;Set the toolbar buttons
invoke GetDlgItem,hWin,IDC_TBR
invoke DoToolBar,hInstance,eax
;Set FileName to NewFile
invoke lstrcpy,offset FileName,offset NewFile
invoke SetWinCaption
;Get handle of RichEdit window and give it focus
invoke GetDlgItem,hWin,IDC_RED
mov hREd,eax
invoke SendMessage,hREd,EM_SETTEXTMODE,0,TM_PLAINTEXT
;Set event mask
invoke SendMessage,hREd,EM_SETEVENTMASK,0,ENM_SELCHANGE
;Set the text limit. The default is 64K
invoke SendMessage,hREd,EM_LIMITTEXT,-1,0
;Create font
invoke lstrcpy,offset lfnt.lfFaceName,offset szFont
mov lfnt.lfHeight,-12
mov lfnt.lfWeight,400
invoke CreateFontIndirect,offset lfnt
mov hFont,eax
;Set font & format
invoke SetFormat,hREd
;Init RichEdit
invoke SendMessage,hREd,EM_SETMODIFY,FALSE,0
invoke SendMessage,hREd,EM_EMPTYUNDOBUFFER,0,0
invoke SetFocus,hREd
.elseif eax==WM_COMMAND
;Menu and toolbar has the same ID's
mov eax,wParam
and eax,0FFFFh
.if eax==IDM_FILE_NEW
invoke WantToSave
.if !eax
invoke SetWindowText,hREd,offset szNULL
invoke lstrcpy,offset FileName,offset NewFile
invoke SetWinCaption
.endif
invoke SetFocus,hREd
.elseif eax==IDM_FILE_OPEN
invoke WantToSave
.if !eax
invoke OpenEdit
.endif
invoke SetFocus,hREd
.elseif eax==IDM_FILE_SAVE
invoke SaveEdit
invoke SetFocus,hREd
.elseif eax==IDM_FILE_SAVEAS
invoke SaveEditAs
invoke SetFocus,hREd
.elseif eax==IDM_FILE_PRINT
.elseif eax==IDM_FILE_EXIT
invoke SendMessage,hWin,WM_CLOSE,0,0
.elseif eax==IDM_EDIT_UNDO
invoke SendMessage,hREd,EM_UNDO,0,0
.elseif eax==IDM_EDIT_REDO
invoke SendMessage,hREd,EM_REDO,0,0
.elseif eax==IDM_EDIT_DELETE
invoke SendMessage,hREd,EM_REPLACESEL,TRUE,0
.elseif eax==IDM_EDIT_CUT
invoke SendMessage,hREd,WM_CUT,0,0
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
69
.elseif eax==IDM_EDIT_COPY
invoke SendMessage,hREd,WM_COPY,0,0
.elseif eax==IDM_EDIT_PASTE
invoke SendMessage,hREd,WM_PASTE,0,0
.elseif eax==IDM_EDIT_SELECTALL
mov chrg.cpMin,0
mov chrg.cpMax,-1
invoke SendMessage,hREd,EM_EXSETSEL,0,addr chrg
.elseif eax==IDM_EDIT_FIND
.if hFind==0
invoke CreateDialogParam,hInstance,IDD_FINDDLG,hWin,offset
FindDlgProc,FALSE
.else
invoke SetFocus,hFind
.endif
.elseif eax==IDM_EDIT_REPLACE
.if hFind==0
invoke CreateDialogParam,hInstance,IDD_FINDDLG,hWin,addr
FindDlgProc,TRUE
.else
invoke SetFocus,hFind
.endif
.elseif eax==IDM_EDIT_FINDNEXT
mov al,findbuff
.if al
invoke Find,FR_DOWN
.endif
.elseif eax==IDM_EDIT_FINDPREV
mov al,findbuff
.if al
invoke Find,0
.endif
.elseif eax==IDM_VIEW_TOOLBAR
invoke GetDlgItem,hWin,IDC_TBR
mov hCtl,eax
xor fView,1
mov eax,fView
and eax,1
.if eax
invoke ShowWindow,hCtl,SW_SHOWNA
.else
invoke ShowWindow,hCtl,SW_HIDE
.endif
invoke SendMessage,hWin,WM_SIZE,0,0
.elseif eax==IDM_VIEW_STATUSBAR
invoke GetDlgItem,hWin,IDC_SBR
mov hCtl,eax
xor fView,2
mov eax,fView
and eax,2
.if eax
invoke ShowWindow,hCtl,SW_SHOWNA
.else
invoke ShowWindow,hCtl,SW_HIDE
.endif
invoke SendMessage,hWin,WM_SIZE,0,0
.elseif eax==IDM_OPTION_FONT
invoke RtlZeroMemory,addr cf,sizeof cf
mov cf.lStructSize,sizeof cf
mov eax,hWin
mov cf.hwndOwner,eax
mov cf.lpLogFont,offset lfnt
mov cf.Flags,CF_SCREENFONTS or CF_EFFECTS or CF_INITTOLOGFONTSTRUCT
mov eax,rgb
mov cf.rgbColors,eax
invoke ChooseFont,addr cf
.if eax
invoke DeleteObject,hFont
invoke CreateFontIndirect,offset lfnt
mov hFont,eax
mov eax,cf.rgbColors
mov rgb,eax
invoke SetFormat,hREd
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
70
.endif
invoke SetFocus,hREd
.elseif eax==IDM_HELP_ABOUT
invoke ShellAbout,hWin,offset AppName,offset AboutMsg,hIcon
invoke SetFocus,hREd
.elseif eax==IDM_ENVIAR_DATO
invoke SendMessage,hREd,EM_GETSELTEXT,0,addr findbuff
mov edx,offset findbuff
mov al,byte ptr [edx]
mov Dato,al
sub Dato,30h
mov Dato,al
invoke Enviar,addr Dato
.elseif eax==IDM_RECIBIR_DATO
mov eax,0
invoke Recibir,addr Dato
mov dl,Dato;byte ptr [edx]
mov dh,0
invoke dwtoa,edx,addr Ristra
IF DEBUG
; En modo Depuración se muestra mensaje con el dato recibido
invoke MessageBox,hWnd,addr Ristra,offset msgDatoRecibido,MB_OK
ENDIF
.elseif eax==IDM_TEST
invoke TestPuerto
.elseif eax==IDM_COMPILAR
invoke Compila,hWnd,hREd,hMainHeap,addr MatrizDeSintaxis
.elseif eax==IDM_COMPILAR_LINEA
invoke SendMessage,hREd,EM_GETLINE,edx,addr buffer
invoke MessageBox,hWnd,addr buffer,addr buffer,MB_OK
invoke atodw,addr buffer
invoke CompilaLinea,addr buffer,addr MatrizDeSintaxis,hWnd
.endif
.elseif eax==WM_NOTIFY
mov edx,lParam
mov eax,(NMHDR ptr [edx]).code
.if eax==TTN_NEEDTEXT
;Toolbar tooltip
mov edx,(NMHDR ptr [edx]).idFrom
invoke LoadString,hInstance,edx,addr buffer,sizeof buffer
lea eax,buffer
mov edx,lParam
mov (TOOLTIPTEXT ptr [edx]).lpszText,eax
.elseif wParam==IDC_RED
;Auto horizontal scroll text into view
invoke GetCaretPos,addr pt
invoke SendMessage,hREd,EM_GETRECT,0,addr rect
mov eax,rect.right
sub eax,pt.x
.if eax<20
inc rect.left
inc rect.top
sub rect.right,70
invoke SendMessage,hREd,EM_SETRECT,0,addr rect
invoke SendMessage,hREd,EM_SCROLLCARET,0,0
add rect.right,70
invoke SendMessage,hREd,EM_SETRECT,0,addr rect
.endif
.endif
.elseif eax==WM_SIZE
mov eax,fView
and eax,1
.if eax
;Resize toolbar
invoke GetDlgItem,hWin,IDC_TBR
mov hCtl,eax
invoke MoveWindow,hCtl,0,0,0,0,TRUE
;Get height of toolbar
invoke GetWindowRect,hCtl,addr rect
mov eax,rect.bottom
sub eax,rect.top
.endif
push eax
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
71
mov eax,fView
and eax,2
.if eax
;Resize statusbar
invoke GetDlgItem,hWin,IDC_SBR
mov hCtl,eax
invoke MoveWindow,hCtl,0,0,0,0,TRUE
;Get height of statusbar
invoke GetWindowRect,hCtl,addr rect
mov eax,rect.bottom
sub eax,rect.top
.endif
push eax
;Get size of windows client area
invoke GetClientRect,hWin,addr rect
;Subtract height of statusbar from bottom
pop eax
sub rect.bottom,eax
;Add height of toolbar to top
pop eax
add rect.top,eax
;Get new height of RichEdit window
mov eax,rect.bottom
sub eax,rect.top
mov ht,eax
;Resize RichEdit window
invoke MoveWindow,hREd,0,rect.top,rect.right,ht,TRUE
.elseif eax==WM_CLOSE
invoke WantToSave
.if !eax
invoke DestroyWindow,hWin
.endif
.elseif eax==WM_DESTROY
invoke DeleteObject,hFont
invoke PostQuitMessage,NULL
.elseif eax==WM_CONTEXTMENU
mov eax,wParam
.if eax==hREd
mov eax,lParam
and eax,0FFFFh
mov pt.x,eax
mov eax,lParam
shr eax,16
mov pt.y,eax
invoke GetMenu,hWin
invoke GetSubMenu,eax,1
invoke TrackPopupMenu,eax,TPM_LEFTALIGN or TPM_RIGHTBUTTON,pt.x,pt.y,0,hWin,0
xor eax,eax
ret
.endif
.else
invoke DefWindowProc,hWin,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
WndProc endp
end start
6.2.3. Interfaz del Intérprete de Comandos (TraduceIns.inc)
INSTRUCCION struct
longitud dd ? ; Longitud de la instruccion para una comparación más rápida
instruccion dd ? ; Nombre de la instruccion en lenguaje natural
pos db ? ; Número que identifica a la instrucción. Para acceder a ella
nent db ? ; Número de parámetros de entrada
nsal db ? ; Número de parámetros de salida
color dd ? ; Puntero al Color
sig dd ? ; Puntero a la siguiente estructura tipo instruccion
INSTRUCCION ends
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
72
;Numero de elementos de la estructura
numcamp equ 7
RellenaListaIns PROTO :DWORD,:DWORD
6.2.4. Intérprete de Comandos (TraduceIns.asm)
.386
.model flat,stdcall
option casemap:none
include shlwapi.inc ;Para el manejo de Ristras
include kernel32.inc
include user32.inc
include WINDOWS.INC
include TraduceIns.inc
includelib shlwapi.lib
includelib kernel32.lib
includelib user32.lib
.data
WordFileName db "\reglasComp.txt",0
ASMSection db "REGLAS",0
I1Clave db "LeerPuertoA",0
I2Clave db "EscribirPuertoB",0
I3Clave db "LeerTeclado",0
I4Clave db "EscribirBus",0
I5Clave db "LeerBus",0
I6Clave db "EscribirNBus",0
I7Clave db "LeerNBus",0
I8Clave db "EnviarComandoLCD",0
I9Clave db "EnviarDatoLCD",0
I10Clave db "LeerPuertoB",0
I11Clave db "Mostrar",0
I12Clave db "Asignar",0
ZeroString db 0
ASMColorArray dd 0h,0805F50h,0FFh,666F00h,44F0h,5F8754h,6 dup(0FF0000h)
CommentColor dd 808000h
.data?
MatrizDeSintaxis dd ?
.code
RellenaEstructuras proc uses edi esi hHeap:DWORD,pBuffer:DWORD, nSize:DWORD,
ArrayOffset:DWORD,pArray:DWORD
LOCAL pStruct: DWORD ;Puntero a la zona de memoria donde almacenaremos nuestra estructura
;Inicializamos la variables
mov edi,pBuffer
invoke CharLower,edi
mov ecx,nSize
;Almacenaremos el Nombre de la Instruccion, el tamaño y el color
AlmacenaNomIns:
invoke StrChrI,pBuffer," "
mov byte ptr [eax],0
inc eax
mov esi,eax ;Guardamos el principio del primer dato
;Reservamos espacio para nuestra estructura
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof INSTRUCCION
push esi
mov pStruct,eax
;Almacenamos la longitud de la Palabra
invoke lstrlen,edi
mov esi,pStruct
assume esi:ptr INSTRUCCION
mov [esi].longitud,eax
;Caragamos un puntero al color
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
73
push ArrayOffset
pop [esi].color
;Reservamos espacio para guardar la palabra y lo inicializamos a 0
inc eax
invoke HeapAlloc,hHeap, HEAP_ZERO_MEMORY,eax
mov [esi].instruccion,eax ;Guardamos en instruccion la direccion de la memoria reservada
mov edx,eax
invoke lstrcpy,edx,edi ;Copiamos la ristra a donde acabamos de reservar memoria
mov eax,pArray
.if dword ptr [eax]==0
mov dword ptr [eax],esi
.else
push dword ptr [eax]
pop [esi].sig
mov dword ptr [eax],esi
.endif
pop esi
;Almacenaremos el Numero de la Instruccion
AlmacenaNumIns:
mov edi,esi
invoke StrChrI,edi," "
mov byte ptr [eax],0
inc eax
mov esi,eax
invoke StrToInt,edi
push esi
mov esi,pStruct
mov esi,pStruct
assume esi: ptr INSTRUCCION
mov [esi].pos,al
pop esi
;Almacenamos el numero de parámetros de entrada
AlmacenaNumParamEnt:
mov edi,esi
invoke StrChrI,edi," "
mov byte ptr [eax],0
inc eax
mov esi,eax
invoke StrToInt,edi
push esi
mov esi,pStruct
assume esi: ptr INSTRUCCION
mov [esi].nent,al
pop esi
;Almacenamos el numero de parámetros de salida
AlmacenaNumParamSal:
mov edi,esi
invoke StrToInt,edi
push esi
mov esi,pStruct
assume esi: ptr INSTRUCCION
mov [esi].nsal,al
pop esi
Terminar:
ret
RellenaEstructuras endp
RellenaListaIns proc uses edi hInstance:HINSTANCE, hMainHeap:DWORD
LOCAL buffer[1024]:BYTE
LOCAL pTemp:DWORD
LOCAL BlockSize:DWORD
;========================================================
; Inicializamos a Cero la Memoria
;========================================================
;invoke RtlZeroMemory,addr MatrizDeSintaxis,sizeof MatrizDeSintaxis
;========================================================
; Obtenemos el Path del fichero ejecutable y le añadimos el fichero
; de sintaxis
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
74
@@:
;========================================================
invoke GetModuleFileName,hInstance,addr buffer,sizeof buffer
invoke lstrlen,addr buffer
mov ecx,eax
dec ecx
lea edi,buffer
add edi,ecx
std
mov al,"\"
repne scasb
cld
inc edi
mov byte ptr [edi],0
invoke lstrcat,addr buffer,addr WordFileName
;========================================================
; Comprobamos que existe el archivo
;========================================================
invoke GetFileAttributes,addr buffer
.if eax!=-1
;========================================================
; Tomar un bloque de memoria de la pila para las ristras
;========================================================
mov BlockSize,1024*10
invoke HeapAlloc,hMainHeap,0,BlockSize
mov pTemp,eax
invoke GetPrivateProfileString,addr ASMSection,addr I1Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
inc eax
mov edx,offset ASMColorArray
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I2Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,4
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I3Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,8
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I4Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,12
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I5Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,16
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I6Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,20
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I7Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,24
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
75
@@:
.endif
invoke GetPrivateProfileString,addr ASMSection,addr I8Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,28
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I9Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,32
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I10Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,36
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I11Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,40
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
@@:
invoke GetPrivateProfileString,addr ASMSection,addr I12Clave,addr ZeroString,pTemp,BlockSize,addr buffer
.if eax!=0
mov edx,offset ASMColorArray
add edx,44
invoke RellenaEstructuras,hMainHeap,pTemp,eax,edx,addr MatrizDeSintaxis
.endif
invoke HeapFree,hMainHeap,0,pTemp
.endif
mov eax,offset MatrizDeSintaxis
ret
RellenaListaIns endp
end
6.2.5. Fichero de Reglas de Sintaxis (reglasComp.txt)
Estas son las reglas de los comandos y sus parámetros de entrada y salida:
;El significado de las instrucciones es el siguiente:
INS PosInstr NºParamEnt NºParamSal
[REGLAS]
LeerPuertoA= LeerPuertoA 0 0 1
LeerPuertoB= LeerPuertoB 1 0 1
EscribirPuertoA= EscribirPuertoA 2 1 0
EscribirPuertoB= EscribirPuertoB 3 1 0
LeerTeclado= LeerTeclado 4 0 1
EscribirBus= EscribirBus 5 2 0
LeerBus= LeerBus 6 1 1
EscribirNBus= EscribirNBus 7 3 0
LeerNBus= LeerNBus 8 2 1
EnviarDatoLCD= EnviarDatoLCD 9 1 0
EnviarComandoLCD= EnviarComandoLCD 10 1 0
Mostrar= Mostrar 11 1 0
Asignar= Asignar 12 1 1
; Comandos del LCD; se deben ejecutar siempre y obligatoriamente después de EnviarComandoLCD
[COMANDOSLCD]
LimpiarDisplay= LimpiarDisplay 0 0 0
IrAInicio= IrAInicio 1 0 0
Modo= Modo 2 2 0
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
76
DisplayOnOFF= DisplayOnOFF 3 3 0
Desplazamiento= Desplazamiento 4 2 0
FunctionSet= FunctionSet 5 3 0
DireccionCGRAM= DireccionCGRAM 6 1 0
DireccionDDRAM= DireccionDDRAM 7 1 0
Estado= Estado 8 0 2
EscribirRAM= 9 1 0
LeerRAM= 10 0 1
6.2.6. Interfaz del Compilador de Comandos (Compilador.inc)
Compila PROTO :DWORD, :DWORD, :DWORD, :DWORD
CompilaLinea PROTO :DWORD, :DWORD, :DWORD
Ejecutar PROTO :BYTE, :DWORD, :DWORD, :DWORD
6.2.7. Compilador de Comandos (Compilador.asm)
.386
.model flat,stdcall
option casemap:none
include Compilador.inc
include Protocolo.inc
include TraduceIns.inc
include windows.inc
include user32.inc
include masm32.inc
include kernel32.inc
include shlwapi.inc ;Para el manejo de Ristras
includelib shlwapi.lib
includelib kernel32.lib
includelib user32.lib
includelib masm32.lib
VARIABLES struct
nombre dd ? ;puntero al nombre de la variable ASCII
valor db ?
sig dd NULL ;puntero a la sig estructura
VARIABLES ends
InsertarVariable PROTO :DWORD,:DWORD,:DWORD
ListaVariables PROTO :DWORD,:DWORD
.data?
pVar dd ?
paramEnt db 256 dup(?)
paramSal dd 256 dup(?)
.data
idVar db 'var',0
.code
InsertarVariable proc uses edi hHeap:DWORD,VarActual:DWORD,pVarSig:DWORD
;----------------------------------------------------------------------; hHeap --> Manejador de la Pila del programa
; VarActual --> Dirección donde esté almacenada la ristra de
; la variable actual (se pasa por valor, es de entrada)
; pVarSig --> Puntero a la variable siguiente (se pasa por puntero,
; es de entrada/salida)
; eax --> Devuelve puntero a la estructura de la variable
; Los registros de propósito general se verán alterados tras la llamada
;----------------------------------------------------------------------; PASO 1: Separar variable actual (*VarActual) de variable siguiente (*VarSig)
mov eax,pVarSig
mov eax,[eax]
mov byte ptr [eax],0
; PASO 2: Incrementar el puntero a la variable siguiente (VarSig)
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
77
mov eax,pVarSig
inc dword ptr [eax]
; PASO 3: Reserva de memoria para insertar la variable actual (toma de bloque)
; y se asigna el puntero a pActual (señalado por ppActual)
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,sizeof VARIABLES
assume edi: ptr VARIABLES
mov edi,eax
; PASO 4: Reserva de memoria para el nombre de la variable
invoke lstrlen,VarActual
inc eax
invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,eax
; PASO 5: Creación de estructura (inicializada a 0)
mov [edi].nombre,eax
invoke lstrcpy,[edi].nombre,VarActual
mov [edi].valor,0
mov [edi].sig,NULL
; PASO 6: Retorno del puntero a la estructura de la variable
mov eax,edi
ret
InsertarVariable endp
ListaVariables proc uses edi hHeap:DWORD,Buffer:DWORD
LOCAL VarActual:DWORD ; Puntero al inicio de la variable actual
LOCAL VarSig:DWORD ; Puntero al inicio de la variable siguiente
LOCAL pAnterior:DWORD ; Puntero a la estructura de la variable anterior
; PASO 1: Separación de la directiva de variables y primera variable
invoke StrChrI,Buffer," "
mov byte ptr [eax],0
mov VarActual,eax
inc VarActual
; PASO 2: Comprobamos que es la cabecera de definición de variables (idVar)
invoke lstrcmp,Buffer,addr idVar
.if eax != 0
ret
.endif
mov pAnterior,0
; PASO 3: Inserción de todas las variables en la lista
.while TRUE
; PASO 3.1: Preparación/Búsqueda de la siguiente variable
invoke StrChrI,VarActual," "
.if eax != NULL
; PASO 3.2a: Inserción de la variable actual en la lista
mov VarSig,eax
invoke InsertarVariable,hHeap,VarActual,addr VarSig
.if pAnterior==0
mov pVar,eax
.else
mov edi,pAnterior
assume edi: ptr VARIABLES
mov [edi].sig,eax
.endif
; PASO 3.3a: Actualización del puntero a la estructura de la variable
; anterior (pAnterior) y de la variable actual (VarActual = VarSig)
mov pAnterior,eax
mov eax,VarSig
mov VarActual,eax
.else
; PASO 3.2b: Búsqueda del final de la última variable (siempre se encuentra)
invoke StrChrI,VarActual,0dh
; PASO 3.3b: Inserción de la última variable en la lista
mov VarSig,eax
invoke InsertarVariable,hHeap,VarActual,addr VarSig
.if pAnterior==0
mov pVar,eax
.else
mov edi,pAnterior
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
78
assume edi: ptr VARIABLES
mov [edi].sig,eax
.endif
.break
.endif
.endw
ret
ListaVariables endp
Compila proc uses edx hWnd:DWORD,hREd:DWORD,hHeap:DWORD,MatrizDeSintaxis:DWORD
LOCAL Buffer[256]:BYTE ; Buffer para almacenamiento temporal de cada línea
LOCAL NLineas:DWORD ; Número de Líneas
; PASO 1: Obtener nº líneas
invoke HeapAlloc,hHeap,0,1024*10
invoke SendMessage,hREd,EM_GETLINECOUNT,0,0 ; eax = Número de líneas (0 si no hay ninguna)
.if eax==0
mov eax,1
ret ; ERROR 1: No hay líneas que compilar
.endif
mov NLineas,eax
; PASO 2: Almacenar estructuras de variables
invoke SendMessage,hREd,EM_GETLINE,0,addr Buffer
invoke ListaVariables,hHeap,addr Buffer
; PASO 3: Compilar líneas
mov edx,1
.while edx < NLineas
pusha
invoke SendMessage,hREd,EM_GETLINE,edx,addr Buffer
invoke CompilaLinea,addr Buffer,MatrizDeSintaxis,hWnd
popa
inc edx
.endw
mov eax,0
ret
Compila endp
CompilaLinea proc uses esi edi Linea:DWORD, MatrizDeSintaxis:DWORD, hWnd:DWORD
LOCAL nEnt: BYTE ;Nº parámetros de entrada de la Instrucción
LOCAL nSal: BYTE ;Nº parámetros de salida de la Instrucción
LOCAL pActual:PTR DWORD ;Puntero al elemento actual en el vector de parametros
;de entrada o en el de salida
LOCAL cmd: BYTE ;Servira para elegir que comando se le va a enviar al PIC
LOCAL posPUno: DWORD ;Almacena la posición del primer parámetro de la instruccion
;PASO 1:Guardamos en edi un puntero a la matriz de sintaxis
mov eax,MatrizDeSintaxis
mov edi,dword ptr[eax]
push edi
;PASO 2:Buscamos la instrucción en nuestra estructura
invoke lstrlen,Linea
mov edx,eax
.if edx > 0
mov esi,Linea ;esi--> Puntero a la ristra
;PASO 2.a: Avanzamos hasta el primer espacio en blanco
invoke StrChrI,Linea," "
mov byte ptr [eax],0 ;Ponemos el final de ristra y ahora comparamos
inc eax ;avanzamos
mov posPUno,eax ;Guardamos la posición del primer parámetro
invoke CharLower,Linea
invoke lstrlen,Linea
mov edx,eax
;PASO 2.b: Una vez separado el nombre de la instruccion recorremos la lista
;en busca de esa instruccion
pop edi
assume edi:ptr INSTRUCCION
.while edi != 0
.if edx == [edi].longitud
push edx
invoke lstrcmpi,[edi].instruccion,esi
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
79
pop edx
.if eax == 0
jmp LeeDatosInstruccion
.endif
.endw
.endif
push [edi].sig ;Avanzamos a la siguiente estructura
pop edi ;
EsmsDeInsNoValida:
ret
LeeDatosInstruccion:
;PASO 3: Almacenamos en nuestras variables locales los datos de la instruccion
mov al,[edi].pos
mov cmd,al
mov al,[edi].nent
mov nEnt,al
mov al,[edi].nsal
mov nSal,al
LeeParametrosDeEntrada:
;PASO 4: Leemos los parámetros de entrada
mov ecx,posPUno
xor eax,eax
mov eax,offset paramEnt
mov pActual,eax
mov dl,nEnt ;dl -> Nº de parámetros de entrada para saber cuantas variables debemos leer
.while dl > 0
push edx
mov esi,ecx ;copiamos la direccion en esi
invoke StrChrI,esi," " ;volvemos a encontrar el primer espacio en blanco
.if eax == NULL ;Si solo hay parámetros de entrada
invoke StrChrI,esi,13
.endif
mov byte ptr [eax],0
inc eax
push eax ;Guardamos en la pila--> Comienzo del sig parámetro
;PASO 4.a: Primero miramos si es un numero
.if (byte ptr [esi] <= "9") && (byte ptr [esi] >= "0")
invoke StrToInt,esi
mov edx,dword ptr[pActual]
mov byte ptr[edx],al
;PASO 4.b: Si no es un número buscamos de que variable se trata
.else
mov edi,pVar
assume edi: ptr VARIABLES
.repeat
invoke lstrcmpi,esi,[edi].nombre
.if eax == 0
.break
.endif
mov edi,[edi].sig
.until edi == NULL
;PASO 4.b.1: Si la variable existe copiamos en el vector de parámetros
;de entrada su valor
.if eax == 0
mov al,[edi].valor
mov edx, dword ptr[pActual]
mov byte ptr[edx],al
.else
ret
.endif
.endif
inc pActual
pop ecx ;-->Recuperamos el comienzo del siguiente parámetro
pop edx ;-->Recuperamos el numero de parámetros que debemos leer
dec dl
.endw
LeeParametrosDeSalida:
;PASO 5: Leemos los parámetros de salida. Tenemos que
;ecx->Posición del primer parametro de salida
mov eax,offset paramSal
mov pActual,eax
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
80
mov dl,nSal
.while dl > 0
push edx
mov esi,ecx ;copiamos la direccion en esi
invoke StrChrI,esi," " ;Volvemos a encontrar el primer espacio en blanco
.if eax == NULL ;Si solo hay parámetros de entrada
invoke StrChrI,esi,13
.endif
mov byte ptr [eax],0
inc eax
push eax ;Guardamos en la pila --> Comuenzo del sig parámetro
;PASO 5.a: Buscamos de que variable se trata
mov edi,pVar
assume edi: ptr VARIABLES
.repeat
invoke lstrcmpi,esi,[edi].nombre
.if eax == 0
.break
.endif
mov edi,[edi].sig
.until edi == NULL
;PASO 5.a.1: Si la variable existe, guardamos la dirección del valor de la
;variable en el vector de parámetros de salida.
.if eax == 0
lea eax,[edi].valor
mov edx,pActual
mov dword ptr[edx],eax
.else
ret
.endif
inc pActual
pop ecx ;-->Recuperamos la posicion del siguiente parámetro
pop edx ;-->Recuperamos el numero de parámetros que debemos leer
dec dl
.endw
;-----------------------------------------------------; Ejecutamos
;-----------------------------------------------------invoke Ejecutar,cmd,addr paramEnt,addr paramSal,hWnd
.endif
ret
CompilaLinea endp
Ejecutar proc uses edx Comando:BYTE, Entrada:DWORD, Salida:DWORD, hWnd:DWORD
LOCAL buffer[2]: BYTE
mov dl,Comando
; Enviar al PIC el comando que tiene que servir
.if dl <= 0ah
invoke Enviar,addr Comando
.endif
; Se ejecuta el comando indicado
.if dl==00h ; Leer PORTA
mov eax,Salida
mov eax,dword ptr[eax]
invoke Recibir, eax ;Esperar que PIC envie el dato leido
.elseif dl==01h ; Leer PORTB
mov eax,Salida
mov eax,dword ptr[eax]
invoke Recibir, eax ;Esperar que PIC envie el dato leido
;.elseif dl==2 ; Escribir PORTA <-- No visible desde el PIC
.elseif dl==03h ; Escribir PORTB
invoke Enviar,Entrada
.elseif dl==04h ; Leer Teclado
mov eax,Salida
mov eax,dword ptr[eax]
invoke Recibir, eax
.elseif dl==05h ; Escribir valor en bus I2C
; Dato = Dato a Transmitir
mov eax,Entrada
invoke Enviar,eax
; Dato = Direccion Fuente de I2C, para escribir el dato
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
81
inc eax
invoke Enviar,eax
.elseif dl==06h ; Leer valor del bus I2C
;Dato = Direccion Fuente del I2C
invoke Enviar, Entrada
;RETARDO
mov eax,Salida
mov eax,dword ptr[eax]
invoke Recibir, eax
.elseif dl==07h ; Escribir N valores en bus I2C
; eax = N Valores
mov eax,Entrada
mov cl,byte ptr [eax];ecx = N Valores a trasmitir
invoke Enviar,eax
inc eax
;eax = Direccion destino
invoke Enviar,eax
inc eax
;ecx = N Valores a trasmitir
.while cl > 0
invoke Enviar,eax
inc eax
dec cl
.endw
.elseif dl==08h ; Leer N valores del bus I2C
; eax = N Valores
mov eax,Entrada
mov cl,byte ptr [eax];ecx = N Valores a trasmitir
invoke Enviar,eax
inc eax
;eax = Direccion destino
invoke Enviar,eax
mov edx,Salida
mov eax,dword ptr[edx]
;ecx = N Valores a trasmitir
.while cl > 0
invoke Recibir,eax
inc edx
dec cl
.endw
.elseif dl==09h ; Enviar Dato a LCD
;Dato = Dato a transmitir al LCD
invoke Enviar, Entrada
.elseif dl==0ah ; Enviar Comando a LCD
; Entrada = Comando a transmitir
invoke Enviar, Entrada
.elseif dl==0bh; Mostrar
mov eax, Entrada
mov dl, byte ptr[eax]
add dl,30h
lea ecx,buffer
mov byte ptr[ecx], dl
inc ecx
mov byte ptr[ecx], 0
invoke MessageBox,hWnd,addr buffer,addr buffer,MB_OK
.elseif dl == 0ch;Asignar
mov eax,Entrada
mov dl,byte ptr[eax]
mov eax,Salida
mov eax,dword ptr[eax]
mov byte ptr[eax],dl
.endif
ret
Ejecutar endp
end
6.2.8. Interfaz del Acceso al Puerto Paralelo (Paralelo.inc)
externdef RegDato:WORD
externdef RegEstado:WORD
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
82
externdef RegControl:WORD
TomarPuerto PROTO
LeerPuerto PROTO :DWORD
EscribirPuerto PROTO :DWORD
6.2.9. Acceso al Puerto Paralelo (Paralelo.asm)
.386
.model flat,stdcall
option casemap:none
include Paralelo.inc
include WinIo.inc
includelib WinIo.lib
.const
LPT1 equ 0408h
LPT2 equ LPT1+2
LPT3 equ LPT2+2
.data?
RegDato dw ?
RegEstado dw ?
RegControl dw ?
.code
TomarPuerto proc uses edx
;----------------------------------------------; Obtención del puerto paralelo
;----------------------------------------------; Puerto Paralelo:
; LPT1 = 0408h
; LPT2 = 0408h + 2h = 040ah
; LPT3 = 040ah + 2h = 040ch
;
; Los valores típicos de cada registro son:
; RegDato = 0378h
; RegEstado = 0379h
; RegControl = 037ah
; Con la llamada a InitializeWinIo se permite el
; acceso a Memoria y Entrada/Salida directo.
; Luego se toma la dirección de los registros
; del puerto paralelo.
;----------------------------------------------invoke InitializeWinIo
invoke GetPhysLong,LPT1,offset RegDato
mov dx,RegDato
inc dx
mov RegEstado,dx
inc dx
mov RegControl,dx
ret
TomarPuerto endp
LeerPuerto proc uses edx Reg:DWORD
;----------------------------------------------; Leer del Puerto Paralelo
;----------------------------------------------mov edx,Reg
mov dx,word ptr [edx]
in al,dx
ret
LeerPuerto endp
EscribirPuerto proc uses edx Reg:DWORD
;----------------------------------------------; Escribir en el Puerto Paralelo
;----------------------------------------------mov edx,Reg
mov dx,word ptr [edx]
out dx,al
ret
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
83
EscribirPuerto endp
end
6.2.10. Interfaz del Protocolo del Puerto Paralelo (Protocolo.inc)
Inicializar PROTO
Enviar PROTO :DWORD
Recibir PROTO :DWORD
TestPuerto PROTO
6.2.11. Protocolo del Puerto Paralelo (Protocolo.asm)
.386
.model flat,stdcall
option casemap:none
include Paralelo.inc
include Protocolo.inc
.const
PC_LISTO equ 00000001b ; Bit del Puerto Paralelo que indica el estado del PC (usa logica inversa)
PIC_LISTO equ 01000000b ; Bit del Puerto Paralelo que indica el estado del PIC (no usa logica inversa)
C5 equ 00100000b ; Bit del puerto bidireccional (C5 == 1 --> Entrada (E/S); C5 == 0 --> Salida)
.code
Inicializar proc
;----------------------------------------------; Inicializa el Puerto Paralelo
;----------------------------------------------; Se toman los registros del Puerto Paralelo y
; se asegura que esté en modo bidireccional.
;----------------------------------------------invoke TomarPuerto
; Desactivación del modo bidireccional, para que inicialmente
; sea de salida (y no tome valores que le entren, lo que se usara
; en Enviar)
invoke LeerPuerto,offset RegControl
mov ah,C5
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
; Se pone a 0ffh el puerto de Datos para que no afecte
mov al,0ffh
invoke EscribirPuerto,offset RegDato
invoke LeerPuerto,offset RegControl
or al,PC_LISTO ; Se pone a 1 (en realidad 0) al(PC_LISTO)
invoke EscribirPuerto,offset RegControl
ret
Inicializar endp
Enviar proc Dato:DWORD
;----------------------------------------------; Envía Dato de PC al PIC
;----------------------------------------------; Realiza el envío del byte Dato desde el PC
; al PIC, según el protocolo.
;----------------------------------------------; PASO 1: Esperar a que el PIC esté listo para recibir el Dato (Estado de Reposo)
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until !al ; El PIC estará listo cuando al(PIC_LISTO) == 0
; PASO 2: El PC pone el Dato en el puerto (se lo envía al PIC)
; (primero se pone el puerto de salida; aunque normalmente
; no es necesario hacerlo, pues es el estado por defecto)
invoke LeerPuerto,offset RegControl
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
84
mov ah,C5
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
mov eax,Dato
mov al,byte ptr [eax] ; No hay que controlar
invoke EscribirPuerto,offset RegDato
; PASO 3: Indicación de que PC va a transmitir al PIC
invoke LeerPuerto,offset RegControl
mov ah,PC_LISTO
not ah
and al,ah ; Se pone a 0 (en la linea a 1) al(PC_LISTO)
invoke EscribirPuerto,offset RegControl
; PASO 4: Esperar a que el PIC indique que ha recibido correctamente el Dato
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until al ; El PIC indica la toma del dato con al(PIC_LISTO) == 1
; PASO 5: El PC deja de indicar que transmite el Dato al PIC, pues se terminó
; la transmisión, permitiendo que el PIC comience a trabajar
invoke LeerPuerto,offset RegControl
or al,PC_LISTO ; Se pone a 1 (en realidad 0) al(PC_LISTO)
invoke EscribirPuerto,offset RegControl
; PASO 6: Esperar a que el PIC esté de nuevo en Estado de Reposo, que es realmente de
; ejecución del comando/instrucción. Este paso es opcional, pero es más seguro hacerlo
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until al ; El PIC estará en reposo (ejecutando el comando) cuando al(PIC_LISTO) == 0
; FINAL: Ahora el PIC procesará el comando/instrucción asociada al comando,
; mientras el PC espera que el usuario solicite la ejecución de otro nuevo
ret
Enviar endp
Recibir proc uses edx Dato:DWORD
;----------------------------------------------; Recibe Dato desde PIC
;----------------------------------------------; Realiza la recepción del byte Dato enviado
; desde el PIC, devolviéndolo en Dato, según el
; protocolo.
;----------------------------------------------; PASO 1: Poner PC en estado de envio (PC_LISTO == 1); a 0 en la linea
invoke LeerPuerto,offset RegControl
or al,PC_LISTO
invoke EscribirPuerto,offset RegControl
; PASO 2: Se pone el puerto de Datos de Entrada (Entrada/Salida)
invoke LeerPuerto,offset RegControl
or al,C5
invoke EscribirPuerto,offset RegControl
; PASO 3: Esperar a que el PIC indique el Dato está listo para recibirse
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until al ; El PIC indica que el dato está listo con al(PIC_LISTO) == 1 sin logica negada
; PASO 4: El PC lee el Dato
invoke LeerPuerto,offset RegDato
mov edx,Dato
mov byte ptr [edx],al
invoke LeerPuerto,offset RegControl
mov ah,C5
not ah
and al,ah
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
85
invoke EscribirPuerto,offset RegControl
; PASO 5: El PC indica que ya recibió el Dato (PC_LISTO == 0)
invoke LeerPuerto,offset RegControl
mov ah,PC_LISTO
not ah
and al,ah
invoke EscribirPuerto,offset RegControl
; PASO 6: El PC espera que el PIC deje de enviar el Dato (a que se de cuenta
; de que el PC ya recibió el Dato)
.repeat
invoke LeerPuerto,offset RegEstado
and al,PIC_LISTO
.until !al ; El PIC finalizará la transmisión, con al(PIC_LISTO) == 0 sin logica negada
; PASO 7: Poner PC en estado de no reposo; a 0 en la linea
invoke LeerPuerto,offset RegControl
or al,PC_LISTO
invoke EscribirPuerto,offset RegControl
; FINAL: Ahora el PC ha recibido el Dato y lo procesará, mientras el PIC estará
; en reposo, sin transmitir, pero podrá estar en la ejecución de un comandp
ret
Recibir endp
TestPuerto proc uses eax ecx edx
;----------------------------------------------; Bucle de testeo para el puerto Paralelo
;----------------------------------------------; Direcciones standard del puerto paralelo:
; Dato --> 378h
; Estado --> 379h
; Control --> 37ah
;----------------------------------------------mov cx,1
; Valores por defecto para puerto (dx) y dato (al)
mov dx, RegDato
mov dx, RegEstado
mov dx, RegControl
mov al,20h ; 20h activaria C5 en RegControl
.while cx==1
out dx,al
in al,dx
.endw
ret
TestPuerto endp
end
6.2.12. Interfaz de la Librería Winio (WinIo.inc)
Las funciones de las que dispone son:
; C:\MASM32\DRO\WinIo.lib PROTOTYPES
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GetPhysLong PROTO :DWORD,:DWORD
GetPortVal PROTO :DWORD,:DWORD,:DWORD
InitializeWinIo PROTO
InstallWinIoDriver PROTO :DWORD,:DWORD
MapPhysToLin PROTO :DWORD,:DWORD,:DWORD
RemoveWinIoDriver PROTO
SetPhysLong PROTO :DWORD,:DWORD
SetPortVal PROTO :DWORD,:DWORD,:DWORD
ShutdownWinIo PROTO
UnmapPhysicalMemory PROTO :DWORD,:DWORD
Trabajo – Emulador de Instrucciones para el PIC, con Comunicación por Puerto Paralelo
86

Documentos relacionados