5.9 Módulo de entrada/salida e interrupciones
El simulador permite configurar la conexión de diversos módulos de entrada/salida y otros dispositivos al bus principal, agrupados en las siguientes categorías:
Teclado y pantalla.
Un módulo PIO, que puede conectarse a LEDs e interruptores.
Módulo Handshake, con posibilidad de conexión a una impresora, con o sin controlador PIC.
Un controlador PIC, que interactúa con la tecla F10 para generar interrupciones, junto con un temporizador y su reloj asociado.
La Figura 5.24 ofrece una representación integral de la arquitectura del simulador, ilustrando la disposición y conectividad de los distintos dispositivos y módulos. Esta visualización facilita la comprensión de la estructura interna y la interacción entre componentes, aportando claridad al análisis de los mecanismos de comunicación y gestión de recursos implementados en el sistema.
Figura 5.24: Arquitectura general del simulador
El componente chipSelect se encarga de activar el dispositivo correspondiente en cada momento. Para ello, recibe las señales de control del CPU junto con el bus de direcciones, y genera las señales de selección de chip (CS) necesarias para habilitar el dispositivo adecuado.
La Tabla 5.16 presenta las categorías de instrucciones diseñadas para la interacción con los módulos de entrada y salida, junto con sus códigos de operación y las acciones correspondientes. Esta síntesis facilita la comprensión de los mecanismos de comunicación y gestión de interrupciones implementados en el simulador.
| Categoría | Instrucción | Código operación | Acción |
|---|---|---|---|
| Transferencia de datos | MOV | {0, 1, 2} | Copiar entre registros, cargar a registro, almacenar en memoria |
| Procesamiento de datos | ADD | {3, 4, 5} | Operación aritmética: operando1 ← operando1 + operando2 |
| SUB | {6, 7, 8} | Operación aritmética: operando1 ← operando1 - operando2 | |
| CMP | {9, 10, 11} | Comparación: operando1 - operando2 (no actualiza el destino) | |
| Control de flujo | JMP / Jxx / CALL / INT | {12} | Salto incondicional JMP, salto condicionales Jxx, subrutina CALL, llamar rutina de interrupción INT |
| Gestión de flujo | HLT / RET / IRET / CLI / STI | {13} | Detener CPU HLT, retorno subrutina RET, retornar de interrupción IRET, deshabilita interrupciones CLI, habilita interrupciones STI |
| Manejo de pila y E/S | OUT / IN / PUSH / POP | {14} | Enviar a puerto OUT, recibir desde puerto IN, poner en la pila PUSH, retirar de la pila POP |
| Miscelánea | AND / OR / XOR / NOT / NEG / INC / DEC | {15} | Operaciones lógicas y aritméticas |
El simulador utiliza un código de operación de 4 bits para las instrucciones, lo que simplifica la arquitectura del sistema pero limita el número máximo de instrucciones implementables a 16 opciones diferentes. Con el objetivo de ampliar el repertorio de instrucciones sin incrementar el tamaño de la codificación, se adoptó una estrategia de agrupación para el código de operación 15.
Bajo esta implementación, las instrucciones lógicas (AND, OR y XOR) comparten el código de operación 15 con las instrucciones aritméticas de un operando (INC, DEC, NEG y NOT). Esta decisión de diseño permite mantener la compatibilidad con los modos de direccionamiento establecidos para las instrucciones aritméticas de dos operandos (ADD, SUB y CMP), garantizando consistencia en la interfaz del simulador mientras se maximiza la funcionalidad dentro de las limitaciones impuestas por el esquema de codificación de 4 bits.
Esta solución representa un compromiso eficaz entre la simplicidad arquitectural y la capacidad funcional del simulador, permitiendo una mayor diversidad de operaciones sin comprometer la claridad pedagógica del diseño.
5.9.1 Etapa de ejecución de instrucciones
Finalmente, se detallan los pasos correspondientes a las instrucciones restantes del repertorio de instrucciones:
- Subrutinas
- CALL
DirecciónMAR\(\leftarrow\)IP– Obtener dirección destino.MBR\(\leftarrow\)read(Memoria[MAR])|IP\(\leftarrow\)IP+ 1 – Leer la dirección e incrementar IP.ri\(\leftarrow\)MBR|SP\(\leftarrow\)SP- 1 - Guardar la dirección en ri y decrementar SP.MAR\(\leftarrow\)SP|MBR\(\leftarrow\)IP- Preparar para apilar.write(Memoria[MAR])\(\leftarrow\)MBR|IP\(\leftarrow\)ri- Guardar IP en la pila y saltar a la subrutina.
- RET
MAR\(\leftarrow\)SP– Obtener dirección de retorno desde la pila.MBR\(\leftarrow\)read(Memoria[MAR])– Leer la dirección de retorno.IP\(\leftarrow\)MBR|SP\(\leftarrow\)SP+ 1 – Restaurar IP y actualizar SP.
- CALL
- Interrupciones
- INT
DirecciónMAR\(\leftarrow\)IP– Obtener dirección destino.MBR\(\leftarrow\)read(Memoria[MAR])|IP\(\leftarrow\)IP+ 1 – Leer la dirección e incrementar IP.ri\(\leftarrow\)MBR|SP\(\leftarrow\)SP- 1 – Guardar la dirección en ri y decrementar SP.MAR\(\leftarrow\)SP|MBR\(\leftarrow\)Flags– Preparar para apilar los registros de estado.write(Memoria[MAR])\(\leftarrow\)MBR| update(Flags I=0) – Guardar los registros de estado y desactivar interrupciones.MBR\(\leftarrow\)IP|SP\(\leftarrow\)SP- 1 – Guardar IP en la pila.MAR\(\leftarrow\)SP– Preparar para apilar.write(Memoria[MAR])\(\leftarrow\)MBR- Guardar en la pila.MAR\(\leftarrow\)ri- Obtener dirección rutina.MBR\(\leftarrow\)read(Memoria[MAR])– Leer la dirección.IP\(\leftarrow\)MBR– Asigno dirección de la rutina de interrupción.
- IRET
MAR\(\leftarrow\)SP– Obtener dirección de retorno desde la pilaMBR\(\leftarrow\)read(Memoria[MAR])– Leer la dirección de retorno.IP\(\leftarrow\)MBR|SP\(\leftarrow\)SP+ 1 – Restaurar IP y actualizar SP.MAR\(\leftarrow\)SP– Preparar para leer los registros de estado.MBR\(\leftarrow\)read(Memoria[MAR])– Leer los registros de estadoFlags\(\leftarrow\)MBR|SP\(\leftarrow\)SP+ 1 – Restaurar los registros de estado y actualizar SP.
- CLI
- update(Flags I=0) – Desactivar interrupciones.
- STI
- update(Flags I=1) – Activar interrupciones.
- INT
- E/S
- OUT
- Direccion destino en registro OUT DL, AL
MAR\(\leftarrow\)DL– Obtener dirección destino.MBR\(\leftarrow\)AL– Preparar el contenido a escribir.write(E-S[MAR])\(\leftarrow\)MBR– Escribir el contenido en el puerto de E/S.
- Direccion destino inmediato OUT inmediato, AL
MAR\(\leftarrow\)IP– Obtener dirección del dato inmediato.MBR\(\leftarrow\)read(Memoria[MAR])|IP\(\leftarrow\)IP+ 1 – Leer el dato e incrementar IP.MAR\(\leftarrow\)MBR– Preparar la dirección destino.MBR\(\leftarrow\)AL– Preparar el contenido a escribir.write(E-S[MAR])\(\leftarrow\)MBR– Escribir el contenido en el puerto de E/S.
- Direccion destino en registro OUT DL, AL
- IN
- Direccion destino en registro IN AL, DL
MAR\(\leftarrow\)DL– Obtener dirección del puerto de entrada.MBR\(\leftarrow\)read(E-S[MAR])– Leer el contenido del puerto.AL\(\leftarrow\)MBR– Almacenar el contenido en AL.
- Direccion destino inmediato IN AL, inmediato
MAR\(\leftarrow\)IP– Obtener dirección del dato inmediato.MBR\(\leftarrow\)read(Memoria[MAR])|IP\(\leftarrow\)IP+ 1 – Leer el dato e incrementar IP.MAR\(\leftarrow\)MBR– Preparar la dirección del puerto de entrada..MBR\(\leftarrow\)read(E-S[MAR])– Leer el contenido del puerto.AL\(\leftarrow\)MBR– Almacenar el contenido en AL.
- Direccion destino en registro IN AL, DL
- OUT
- Pila
- POP Rx
MAR\(\leftarrow\)SP– Obtener dirección de la pila.MBR\(\leftarrow\)read(Memoria[MAR])– Leer el contenido de la pila.Rx\(\leftarrow\)MBR|SP\(\leftarrow\)SP+ 1 – Almacenar el contenido en Rx y actualizar SP.
- PUSH Ry
MBR\(\leftarrow\)Ry|SP\(\leftarrow\)SP- 1 – Preparar el contenido a escribir y actualizar SP.
MAR\(\leftarrow\)SP– Preparar la dirección de la pila.write(Memoria[MAR])\(\leftarrow\)MBR– Escribir el contenido en la pila.
- POP Rx
- Operaciones Lógicas y Aritméticas
Operaciones con dos operandos (AND, OR, XOR)
Siguen los mismos pasos de ejecución que las operaciones aritméticas ADD y SUB, con la diferencia en la operación realizada por la ALU.
Operaciones con un operando (NOT, NEG, INC, DEC)
- Destino en registro (
Rx)- INC
RxRx\(\leftarrow\)Rx+ 1 | update(Flags) - Incrementar el valor del registro y actualizar los flags.
- DEC
RxRx\(\leftarrow\)Rx- 1 | update(Flags) - Decrementar el valor del registro y actualizar los flags.
- NOT
RxRx\(\leftarrow\) NOTRx| update(Flags) - Realizar la operación lógica NOT y actualizar los flags.
- NEG
RxRx\(\leftarrow\) CA2Rx| update(Flags) - Realizar la operación de complemento a dos y actualizar los flags.
- INC
- Destino en memoria (
[Dirección]o[BL])- Modo Directo (
[Dirección])MAR\(\leftarrow\)IP– Obtener dirección destino.MBR\(\leftarrow\)read(Memoria[MAR])|IP\(\leftarrow\)IP+ 1 – Leer la dirección e incrementar IP.MAR\(\leftarrow\)MBR– Transferir dirección a MAR.MBR\(\leftarrow\)read(Memoria[MAR])– Leer el dato.- Ejecutar la operación:
- INC:
MBR\(\leftarrow\)MBR+ 1 | update(Flags) - DEC:MBR\(\leftarrow\)MBR- 1 | update(Flags) - NOT:MBR\(\leftarrow\) NOTMBR| update(Flags) - NEG:MBR\(\leftarrow\) CA2MBR| update(Flags) write(Memoria[MAR])\(\leftarrow\)MBR– Escribir en memoria.
- Modo Indirecto (
[BL])MAR\(\leftarrow\)BL– Transferir dirección de destino (en BL) a MAR.MBR\(\leftarrow\)read(Memoria[MAR])– Leer el dato.- Ejecutar la operación (igual que en el caso anterior).
write(Memoria[MAR])\(\leftarrow\)MBR– Escribir en memoria.
- Modo Directo (
- Destino en registro (
5.9.2 Pila y subrutinas
El procesador implementa una pila como método de almacenamiento, accesible tanto por el usuario como por la CPU para su funcionamiento interno. La pila opera bajo el esquema Last In, First Out (LIFO), es decir, el último elemento en ingresar es el primero en salir. Está ubicada en la memoria principal, comenzando en la dirección más alta (FFh) y creciendo hacia direcciones más bajas (FEh, FCh, etc.). El tope de la pila se gestiona mediante el registro SP, y cada elemento almacenado ocupa 8 bits.
Además, el procesador permite el uso de subrutinas, que son fragmentos de código reutilizables y pueden ser invocados desde cualquier parte del programa. Para llamar a una subrutina se utiliza la instrucción [CALL], que apila el valor actual de IP y salta a la dirección de la subrutina, modificando el IP para apuntar a la primera instrucción de la misma. El retorno se realiza mediante la instrucción [RET], que desapila la dirección previamente guardada y restaura el IP, permitiendo continuar la ejecución justo después de la llamada.
Ejemplo de subrutina:
mov al, 1
mov bl, 2
mov cl, 3
call sum3
; ax = 6
hlt
; suma al, bl y cl
sum3: add al, bl
add al, cl
ret
5.9.3 Interrupciones
El simulador VonSim8 integra un teclado y una pantalla como periféricos básicos de entrada y salida, cuya interacción se realiza a través de rutinas de sistema invocadas mediante interrupciones. Esta implementación permite al estudiante experimentar con la gestión de datos y la comunicación entre el procesador y los dispositivos externos, facilitando la comprensión de los mecanismos fundamentales de E/S en una arquitectura computacional. La Figura 5.25 muestra la representación visual de estos periféricos, evidenciando su funcionalidad y su integración en el entorno de simulación.
Figura 5.25: Teclado y pantalla
El vector de interrupciones está preasignado en memoria desde 0x00h hasta 0x07h. Cada entrada ocupa 1 byte y almacena la dirección de inicio de la rutina asociada al número de interrupción. Las interrupciones pueden ser:
De software: instrucción
INT n(n \(\in\) [0,7]).De hardware: generadas por el PIC a través de la línea INTR (requieren
I = 1) véase 5.9.10.
Interrupciones de software (números reservados):
INT 0: termina la ejecución del programa (equivalente aHLT);INT 6: lee un carácter del teclado;INT 7: escribe una cadena de caracteres en pantalla.
Estas entradas están protegidas y no pueden modificarse por el usuario. Las rutinas residen en el monitor del sistema en A0h, B0h y C0h, respectivamente.
Secuencia de atención de una interrupción:
- determinar el número de la interrupción (0-7);
- apilar
Flagsy forzarI = 0; - apilar
IP; - cargar
IP ← MEM[n](dirección desde el vector) y ejecutar la rutina; - finalizar con
IRET, que restauraIPy luegoFlags.
No se admite anidamiento: si llega una nueva solicitud mientras otra está en servicio, queda pendiente en IRR hasta que se envía EOI.
5.9.4 Pantalla
La pantalla es un dispositivo de salida que permite mostrar caracteres. La forma de comunicarse con la pantalla es mediante una llamada al sistema. Esto es así por simplicidad, ya que una pantalla real es mucho más compleja.
Con la llamada INT 7 se escribe una cadena de caracteres en la pantalla. Recibe dos parámetros:
AL: longitud de la cadena a imprimir BL: dirección de memoria donde empieza la cadena
Ejemplo de hola mundo en lenguaje ensamblador para el simulador VonSim8:
cadena DB `Hola!`
MOV BL, offset cadena
MOV AL, 5
INT 7
; Se imprime Hola! (sin las comillas) en la pantalla.
HLT
Hay tres caracteres especiales:
- el carácter de retroceso (
BS, 8 en decimal) borra el carácter previo; - el carácter de salto de línea (
LF, 10 en decimal) imprime, en efecto, un salto de línea — útil para no imprimir todo en una sola línea; - el carácter de form feed (
FF, 12 en decimal) limpia la pantalla.
5.9.5 Teclado
El teclado se modela como un vector de 16 posiciones, cada una capaz de almacenar un carácter ASCII. La pantalla, por su parte, permite visualizar caracteres, facilitando así la comprensión del manejo de entrada y salida de datos en una arquitectura computacional simplificada.
El teclado es un dispositivo de entrada que permite al usuario ingresar caracteres al sistema. La forma de comunicarse con el teclado es mediante una llamada al sistema. Esto es así por simplicidad, ya que un teclado real es mucho más complejo.
Con la llamada INT 6 se detiene la ejecución del código hasta que se presione una tecla en el teclado. El carácter que correspona será guardado en la dirección de memoria almacenada en BL según su representación en ASCII.
car db 0
mov bl, offset car
int 6
hlt
; El carácter escrito se almacenó en 'car'.
; Por ejemplo, si el usuario presionó la tecla 'a', entonces
; se almacena el valor 61h en 'car'.
5.9.6 Puertos de E/S
En VonSim8, la memoria de entrada/salida se encuentra completamente separada de la memoria principal, y su acceso se realiza exclusivamente mediante las instrucciones IN y OUT. Cuando la CPU requiere interactuar con un módulo de E/S, activa la señal IO/M, permitiendo que el selector de chips interprete la dirección en el bus y habilite el dispositivo correspondiente. El espacio de direcciones asignado a la memoria de E/S abarca desde 0x00h hasta 0xFFh, proporcionando un total de 256 direcciones posibles. La Tabla 5.17 presenta las direcciones de entrada/salida disponibles en el simulador, especificando los módulos, registros y nombres asociados, lo que facilita la identificación y gestión de los recursos periféricos en la arquitectura implementada.
| Módulo | Dirección | Nombre |
|---|---|---|
| Timer | 10h | CONT |
| 11h | COMP | |
| PIC | 20h | EOI |
| 21h | IMR | |
| 22h | IRR | |
| 23h | ISR | |
| 24h | INT0 | |
| 25h | INT1 | |
| 26h | INT2 | |
| 27h | INT3 | |
| PIO | 30h | PA |
| 31h | PB | |
| 32h | CA | |
| 33h | CB | |
| Handshake | 40h | DATA |
| 41h | STATE |
5.9.7 Instrucciones IN y OUT
La comunicación con los módulos de entrada/salida se realiza mediante puertos, que son direcciones específicas del procesador. En el simulador, esta interacción se gestiona con dos instrucciones principales:
OUT puerto, AL: envía el contenido deALal puerto indicado (dirección de 8 bits, de00haFFh), permitiendo transmitir datos a dispositivos externos.IN AL, puerto: recibe un byte desde el puerto especificado y lo almacena enAL.
Estas instrucciones permiten al procesador interactuar con una amplia variedad de dispositivos periféricos, facilitando la entrada y salida de datos en el sistema. La correcta utilización de estas instrucciones es esencial para el funcionamiento eficiente del simulador y para la implementación de programas que requieran comunicación con el entorno externo.
5.9.8 Módulo PIO (Leds e Interruptores)
El módulo de entrada/salida programada (Programmed Input-Output, PIO) actúa como interfaz entre la CPU y los dispositivos periféricos genéricos. Su diseño está basado en el controlador PPI 8255 de Intel, específicamente en su modo 0, pero incorpora modificaciones orientadas a simplificar su funcionamiento para fines educativos (Intel Corporation 1979; Godse and Godse 2020).
El módulo cuenta con dos puertos bidireccionales de 8 bits (A y B) que pueden ser configurados de manera independiente. Su arquitectura incluye cuatro registros accesibles:
- PA (dirección 30h en el espacio de memoria E/S): registro de datos del puerto A
- PB (dirección 31h en el espacio de memoria E/S): registro de datos del puerto B
- CA (dirección 32h en el espacio de memoria E/S): registro de configuración del puerto A
- CB (dirección 33h en el espacio de memoria E/S): registro de configuración del puerto B
La configuración de cada puerto se define a través de sus respectivos registros de control (CA y CB). Cada bit del registro de configuración determina la dirección del bit correspondiente en el puerto de datos: un valor 0 configura el bit como salida, mientras que un valor 1 lo configura como entrada. Por ejemplo, si CA = 00001111b, los cuatro bits más significativos del puerto A funcionarán como salidas, y los cuatro bits menos significativos como entradas. El puerto B opera de manera idéntica mediante su registro CB.
Este módulo PIO puede conectarse a diferentes tipos de dispositivos periféricos, como LEDs y interruptores, o a dispositivos más complejos como una impresora, proporcionando así una interfaz versátil para la comunicación con el mundo exterior.
5.9.8.1 Leds
Los diodos emisores de luz (LEDs) funcionan como dispositivos de salida y están conectados al puerto B del módulo PIO. Su control se realiza mediante la manipulación del registro de datos PB (dirección 31h) en conjunto con el registro de configuración CB (dirección 33h).
Para que los LEDs respondan correctamente, es necesario configurar previamente el puerto B como salida escribiendo el valor apropiado en el registro CB. Una vez configurado correctamente el PIO, cualquier modificación en el valor del registro PB se reflejará inmediatamente en el estado de los LEDs correspondientes. En caso de que la configuración del puerto sea incorrecta o se omita este paso, los LEDs permanecerán apagados independientemente de los valores escritos en PB.
Esta configuración permite controlar individualmente cada LED mediante los bits del puerto, ofreciendo flexibilidad para crear patrones luminosos o indicadores visuales en aplicaciones educativas y de demostración.
Las luces o LEDs están conectadas al puerto PB/CB del PIO y funcionan como dispositivos de salida. Su estado solo puede modificarse alterando el valor del puerto PB. Estos cambios se reflejarán en las luces si el PIO está configurado correctamente; de lo contrario, las luces permanecerán apagadas.
; Enciende las luces (una sí, una no): 1010 1010b
; 31h = PB --> puerto de datos para las luces (LEDs)
; 33h = CB --> puerto de control para las luces
; Configura todos los bits de PB como salida para controlar las luces
mov al, 0 ; 0000 0000b: todos los bits de PB en modo salida
out 33h, al ; Escribe en CB para configurar PB como salida
; Enciende las luces alternadas: 1010 1010b (170 decimal)
mov al, 170 ; 1010 1010b: enciende LEDs pares, apaga impares
out 31h, al ; Escribe el valor en PB para actualizar las luces
hlt ; Detiene la ejecución del programa
5.9.8.2 Interruptores
Los interruptores (también denominados llaves) funcionan como dispositivos de entrada conectados al puerto A del módulo PIO. Su operación se gestiona mediante el registro de datos PA (dirección 30h) y el registro de configuración CA (dirección 32h).
Para que los interruptores operen correctamente, es fundamental configurar previamente el puerto A como entrada mediante el registro CA. Una vez establecida la configuración apropiada, cualquier cambio en el estado físico de los interruptores se reflejará automáticamente en los bits correspondientes del registro PA, permitiendo al programa leer su estado actual.
Es importante destacar que la comunicación es unidireccional desde los dispositivos hacia el procesador: aunque es posible escribir valores directamente en el registro PA mediante software, estos cambios no alterarán el estado físico de los interruptores. Los dispositivos de entrada mantienen su estado independientemente de las modificaciones realizadas por programa en sus registros asociados, garantizando así la integridad de la información proveniente del mundo exterior.
; Leer el valor de las llaves como una contraseña hasta que el usuario la adivine
clave db 15 ; Contraseña esperada: 00001111 (en decimal 15)
mensaje_ok db 'Bienvenido!' ; Mensaje a mostrar si la contraseña es correcta
; Configurar PA (Puerto A) como entrada
mov al, 15 ; 00001111b: configura los primeros 4 bits de PA como entrada
out 32h, al ; Escribe en CA para configurar PA
bucle:
in al, 30h ; Lee el valor actual de las llaves desde PA
cmp al, clave ; Compara el valor leído con la contraseña
jz Mostrar_Mensaje ; Si coincide, salta a Mostrar_Mensaje
jmp bucle ; Si no coincide, vuelve a intentar
Mostrar_Mensaje:
mov bl, offset mensaje_ok ; BL apunta al mensaje de éxito
mov al, 11 ; Longitud del mensaje tiene 11 caracteres
int 7
hlt ; Detiene la ejecución del programa
5.9.9 Módulo handshake (Impresora)
El módulo Handshake es un controlador especializado diseñado para facilitar la comunicación con impresoras que utilizan el protocolo Centronics. Su diseño se inspira en el controlador PPI 8255 de Intel, operando en modo “1”, pero incluye modificaciones específicas para simplificar su uso y comprensión en entornos educativos.
El módulo cuenta con una arquitectura simplificada basada en dos registros de 8 bits:
- Registro de datos: ubicado en la dirección 40h del espacio de memoria de E/S.
- Registro de estado: ubicado en la dirección 41h del espacio de memoria de E/S.
La estructura de estos registros se organiza de la siguiente manera:
Datos = DDDD DDDD
Estado = I___ __SB
5.9.9.1 Funcionamiento del registro de datos
El registro de datos almacena el carácter que se desea imprimir, codificado en formato ASCII. Una característica destacada del módulo Handshake es su capacidad de automatización: cada vez que la CPU escribe un valor en este registro, el controlador genera automáticamente un flanco ascendente en la señal strobe, iniciando el proceso de impresión sin necesidad de intervención adicional por parte del software.
5.9.9.2 Funcionamiento del registro de estado
El registro de estado gestiona las señales de control y supervisión del protocolo de comunicación. Sus principales componentes son:
- Bits S y B (strobe y busy): los dos bits menos significativos controlan las señales de comunicación, con diferencias clave respecto a la implementación en el PIO:
- El bit busy es de solo lectura y refleja automáticamente el estado de la impresora
- El bit strobe permanece normalmente en 0 y es gestionado automáticamente por el módulo. Cuando la CPU escribe un 1 en el bit strobe, se genera un flanco ascendente que transmite los datos almacenados en el registro de datos, retornando automáticamente a 0.
- Bit I (interrupción): El bit más significativo controla el sistema de interrupciones del módulo. Cuando este bit está habilitado (I=1) y la impresora se encuentra disponible (B=0), el módulo Handshake genera una interrupción por hardware a través de la línea INT2 del controlador PIC, notificando al sistema que la impresora está lista para recibir nuevos datos.
Esta implementación ofrece una interfaz intuitiva para el control de impresoras, automatizando aspectos críticos del protocolo de comunicación y manteniendo la flexibilidad necesaria para comprender los fundamentos de la interacción con dispositivos periféricos.
5.9.9.3 Ejemplo de uso del módulo Handshake con sondeo
Para imprimir utilizando el módulo Handshake, se deben seguir los siguientes pasos:
- Verificar que el buffer no esté lleno (flag busy).
- Escribir el carácter en el registro de datos.
Además de los caracteres ASCII comunes, el módulo admite caracteres especiales útiles para la impresión:
- Salto de línea (LF, 10 en decimal): Imprime un salto de línea, evitando que todo el texto se imprima en una sola línea.
- Form feed (FF, 12 en decimal): Limpia la impresora, equivalente a arrancar una hoja.
A continuación, se presenta un ejemplo en ensamblador para imprimir la cadena “Hola” utilizando el módulo Handshake:
; Imprime el string 'Hola' en la impresora usando sondeo
dato DB 'Hola', 0 ; String a imprimir, terminado en 0 (carácter nulo)
HS_DATA EQU 40h ; Dirección del registro de datos del Handshake
HS_STATUS EQU 41h ; Dirección del registro de estado del Handshake
; Deshabilita las interrupciones del Handshake (bit 7 en 0)
IN AL, HS_STATUS
AND AL, 01111111b ; Fuerza el bit 7 a 0 (sin interrupciones)
OUT HS_STATUS, AL
; Inicializa el puntero al string
MOV BL, OFFSET dato ; BL apunta al primer carácter del string
; Bucle principal: espera espacio en el buffer e imprime
Sondeo:
IN AL, HS_STATUS
AND AL, 00000001b ; Lee el flag busy (bit 0): 1=lleno, 0=libre
JZ ImprimirCadena ; Si busy=0, hay espacio y puede imprimir
JMP Sondeo ; Si busy=1, espera hasta que haya espacio
ImprimirCadena:
MOV AL, [BL] ; Carga el siguiente carácter del string
CMP AL, 0 ; ¿Es el final del string? (carácter nulo)
JZ fin ; Si sí, termina el programa
OUT HS_DATA, AL ; Envía el carácter al registro de datos del Handshake
INC BL ; Avanza al siguiente carácter del string
JMP Sondeo ; Repite el proceso para el próximo carácter
fin:
HLT ; Detiene la ejecución
Para mejorar la lectura del programa se utilizan constantes en ensamblador “EQU” para definir las direcciones de los puertos del módulo Handshake.
5.9.10 Módulo PIC (Controlador de Interrupciones)
El Programmable Interrupt Controller (PIC) es un módulo que actúa como intermediario entre los dispositivos que generan interrupciones y la CPU. Dado que la CPU dispone de una única línea de entrada para interrupciones, el PIC se encarga de recibir solicitudes de múltiples dispositivos y multiplexarlas en esta línea única.
Este módulo está basado en el PIC 8259A de Intel, aunque se han realizado modificaciones para simplificar su funcionamiento y adaptarlo a fines educativos.
5.9.10.1 Líneas de interrupción
El PIC cuenta con cuatro líneas de interrupción, identificadas como INT0 a INT3 en la configuración actual del simulador. Cada línea está vinculada a un registro de 8 bits en la memoria de entrada/salida; por ejemplo, INT0 se asocia a la dirección 24h, INT1 a la 25h, y así sucesivamente hasta INT3 en la 27h. En estos registros se almacena el número de interrupción correspondiente, permitiendo que el PIC envíe a la CPU la identificación adecuada cuando se produce una solicitud, independizando el número de línea del número de interrupción.
La Tabla 5.18 sintetiza la relación entre las líneas de interrupción y los dispositivos o módulos asociados en el simulador VonSim8, proporcionando una visión estructurada de la asignación y gestión de recursos para la atención de eventos externos. Esta presentación facilita la comprensión del mecanismo de multiplexación y priorización implementado por el PIC.
| Línea | Módulo/Disp. |
|---|---|
| INT0 | Tecla F10 |
| INT1 | [Timer] |
| INT2 | [Handshake] |
| INT3 | – |
5.9.10.2 Registros y puerto EOI
El PIC cuenta con tres registros adicionales para gestionar las interrupciones y un puerto de comando para finalizar su atención. Cada bit de estos registros corresponde a una línea de interrupción; el bit menos significativo (bit 0) corresponde a la línea INT0 y el más significativo (bit 3) a la línea INT3.
Registro IMR (Interrupt Mask Register): Ubicado en la dirección
21hde la memoria de E/S, este registro permite enmascarar (inhabilitar) líneas de interrupción. Si un bit está en 1, la línea correspondiente está enmascarada y no generará interrupciones en la CPU. Si el bit está en 0, la línea está habilitada. Este registro puede ser modificado por la CPU.Registro IRR (Interrupt Request Register): Ubicado en la dirección
22hde la memoria de E/S, este registro indica las interrupciones pendientes. Si un bit está en 1, la línea correspondiente tiene una interrupción pendiente. Este registro es de solo lectura para la CPU y es gestionado por el PIC.Registro ISR (In-Service Register): Ubicado en la dirección
23hde la memoria de E/S, este registro muestra qué interrupción está siendo atendida en un momento dado. Si un bit está en 1, la línea correspondiente está en servicio. Este registro también es de solo lectura para la CPU y es gestionado por el PIC.Puerto EOI (End Of Interrupt): Ubicado en la dirección
20hde la memoria de E/S, es un puerto de solo escritura. Cualquier byte escrito en20hse interpreta como señal de fin de interrupción: el PIC limpia el bit correspondiente en elISRy, si existen solicitudes pendientes no enmascaradas en elIRR, vuelve a activarINTRsegún su política de prioridad. La lectura de20hno está definida.
5.9.10.3 Funcionamiento
Cuando una línea de interrupción se activa, el PIC la registra en el IRR. Si la línea no está enmascarada y no hay otra interrupción en servicio (es decir, si ISR = 00h), el PIC envía una señal de interrupción a la CPU activando la línea INTR.
El proceso de atención de la interrupción sigue estos pasos:
- La CPU responde a la señal
INTRenviando un pulso por la líneaINTA. - Al recibir la señal, el PIC marca la línea como in-service en el registro
ISRy la elimina del registroIRR. - El PIC envía a la CPU, a través del bus de datos, el número de interrupción correspondiente a la línea activa.
- La CPU envía un segundo pulso por la línea
INTA. - El PIC desactiva la línea
INTR.
Para finalizar la atención de la interrupción, la CPU escribe el byte EOI (End of Interrupt) en la dirección 20h de la memoria de E/S. Al recibir este byte, el PIC desmarca la línea como in-service en el registro ISR. Si hay interrupciones pendientes, el PIC reactiva la línea INTR y repite el proceso.
Este PIC no admite interrupciones anidadas. Si ocurre una nueva interrupción mientras otra está siendo atendida, la nueva solicitud se encola en el registro IRR y se procesará una vez que la interrupción en curso haya finalizado, sin importar su prioridad.
5.9.10.4 Prioridades
Cuando hay múltiples interrupciones pendientes, el PIC atiende primero la de mayor prioridad. La prioridad de cada línea está determinada por su número de interrupción: las líneas con números más bajos tienen mayor prioridad. Por ejemplo, la línea INT0 tiene mayor prioridad que la línea INT1.
5.9.10.5 Ejemplo de uso del módulo Handshake con interrupciones
A continuación, se presenta un ejemplo en ensamblador que utiliza el módulo Handshake junto con el PIC para imprimir la cadena “Hola” mediante interrupciones por hardware. En este ejemplo, se configura el PIC para que solo la línea INT2 (asociada al módulo Handshake) esté habilitada, y se define una rutina de interrupción que se ejecuta automáticamente cuando la impresora está lista para recibir un nuevo carácter.
;
; PROGRAMA Impresión de string usando Handshake con interrupciones
; DESCRIPCIÓN Imprime el string 'Hola' en la impresora utilizando el módulo
; Handshake con interrupciones por hardware (INT2)
;
; SECCIÓN DE DATOS
mensaje db 'Hola', 0 ; String a imprimir, terminado en carácter nulo
restantes db 4 ; Contador de caracteres restantes por imprimir
puntero db 0 ; Puntero al siguiente carácter (8 bits)
; CONSTANTES DE HANDSHAKE
HS_DATA EQU 40h ; Registro de datos del Handshake (puerto E-S)
HS_STATUS EQU 41h ; Registro de estado del Handshake (puerto E-S)
; CONSTANTES DE INTERRUPCIONES
ID EQU 2 ; ID de la interrupción para Handshake (0-7)
IMR EQU 21h ; Registro de máscara de interrupciones del PIC
EOI EQU 20h ; Puerto para enviar End Of Interrupt al PIC
INT2 EQU 26h ; Puerto para configurar la línea INT2
; PROGRAMA PRINCIPAL
; 1) CONFIGURACIÓN INICIAL
cli ; Deshabilitar interrupciones globales
; 2) CONFIGURACIÓN DEL HANDSHAKE
; Habilitar interrupciones del Handshake (bit 7 = 1)
in al, HS_STATUS ; Leer estado actual del Handshake
or al, 10000000b ; Activar bit 7 (habilitar interrupciones)
out HS_STATUS, al ; Escribir configuración al Handshake
; 3) CONFIGURACIÓN DEL PIC (Controlador de Interrupciones)
; 3.1) Configurar máscara de interrupciones - Solo habilitar INT2
mov al, 11111011b ; Máscara: habilita solo INT2 (bit 2=0), resto deshabilitado
out IMR, al ; Aplicar máscara al PIC
; 3.2) Asignar ID de interrupción a la línea INT2
mov al, ID ; Cargar ID de interrupción (2)
out INT2, al ; Configurar línea INT2 con este ID
; 3.3) Configurar vector de interrupción en memoria
mov bl, ID ; BL = posición en tabla de vectores (ID=2)
mov [bl], int2_handler ; Almacenar dirección de rutina en vector[2]
; 4) INICIALIZACIÓN DE VARIABLES
mov al, offset mensaje ; AL = dirección del primer carácter del string
mov puntero, al ; Guardar en variable puntero
; 5) ENVIAR PRIMER CARÁCTER PARA INICIAR EL PROCESO
; Esperar que la impresora esté lista
esperar_listo:
in al, HS_STATUS
and al, 00000001b ; Verificar bit busy
jnz esperar_listo ; Si busy=1, esperar
; Enviar primer carácter
mov bl, puntero ; Cargar puntero
mov al, [bl] ; Obtener primer carácter
cmp al, 0 ; Es string vacío
jz fin ; Si está vacío, terminar
out HS_DATA, al ; Enviar primer carácter
inc bl ; Avanzar puntero
mov puntero, bl ; Guardar puntero actualizado
dec restantes ; Decrementar contador
; 6) HABILITAR INTERRUPCIONES Y ESPERAR
sti ; Habilitar interrupciones globales
; 7) BUCLE DE ESPERA
; El programa principal espera hasta que se impriman todos los caracteres
bucle_espera:
cmp restantes, 0 ; Quedan caracteres por imprimir
jnz bucle_espera ; Si quedan, seguir esperando
; 8) FINALIZACIÓN
fin:
hlt ; Detener ejecución del programa
; RUTINA DE INTERRUPCIÓN INT2 - HANDSHAKE
; DESCRIPCIÓN Se ejecuta automáticamente cuando la impresora está lista
; para recibir un nuevo carácter (busy = 0)
; ENTRADA Variable puntero = dirección del siguiente carácter a imprimir
; SALIDA Carácter enviado a la impresora, puntero actualizado
org 80h
int2_handler:
; PRESERVAR CONTEXTO
push al ; Guardar registros que se van a modificar
push bl
; VERIFICAR SI HAY MÁS CARACTERES
cmp restantes, 0 ; Quedan caracteres por imprimir
jz fin_interrupcion ; Si no quedan, terminar
; OBTENER SIGUIENTE CARÁCTER
mov bl, puntero ; BL = puntero al siguiente carácter
mov al, [bl] ; AL = carácter apuntado por BL
cmp al, 0 ; Es el carácter nulo (fin de string)
jz fin_interrupcion ; Si es 0, terminar
; ENVIAR CARÁCTER A LA IMPRESORA
out HS_DATA, al ; Escribir carácter al registro de datos del Handshake
; ACTUALIZAR PUNTEROS Y CONTADORES
inc bl ; Avanzar al siguiente carácter
mov puntero, bl ; Guardar puntero actualizado
dec restantes ; Decrementar contador de caracteres restantes
fin_interrupcion:
; - ENVIAR EOI AL PIC
mov al, 20h ; Señal de fin de interrupción
out EOI, al ; Notificar al PIC
; RESTAURAR CONTEXTO
pop bl ; Restaurar registros preservados
pop al
; RETORNO DE INTERRUPCIÓN
iret ; Retorno de interrupción
5.9.10.6 Tecla F10
La tecla F10 es un dispositivo que habilita una forma rápida y práctica de ejecutar una interrupción por hardware. Está conectada a la línea INT0 del [PIC]. Puede accionarse presionando la tecla F10 el teclado físicamente o haciendo clic en el “botón rojo de interrupción” en la interfaz gráfica.
; Programa Contador de pulsaciones de la tecla F10 usando interrupciones
; 1) Definiciones y variables
cantidad db 0 ; Variable almacena la cantidad de veces que se presionó F10
ID EQU 1 ; ID de la interrupción para F10 (puede ser 0-7)
IMR EQU 21h ; Dirección del registro IMR (máscara de interrupciones)
EOI EQU 20h ; Dirección para enviar End Of Interrupt al PIC
INT0 EQU 24h ; Dirección para configurar la línea INT0 (F10)
; 2) Inicialización del PIC y vector de interrupción
; 2.1) Habilitar solo la interrupción de F10 (INT0)
mov al, 11111110b ; Habilita solo INT0 (bit 0 en 0), el resto deshabilitado
out IMR, al
; 2.2) Configurar el ID de la interrupción para INT0
mov al, ID ; Cargar el ID elegido para F10
out INT0, al
; 2.3) Asociar el vector de interrupción con la subrutina atenderf10
mov bl, ID ; BL = ID de la interrupción
mov [bl], atenderf10 ; Vector de interrupción dirección de la rutina
; 3) Bucle principal (espera activa)
loop: jmp loop ; Espera indefinida (el programa queda esperando interrupciones)
hlt ; (Opcional) Detiene la CPU si sale del bucle
; 4) Rutina de atención de la interrupción F10
org 50h ; Dirección de la subrutina de atención
atenderf10:
inc cantidad ; Incrementa el contador cada vez que se presiona F10
mov al, 20h ; Código de End Of Interrupt (EOI)
out EOI, al ; Notifica al PIC que terminó la atención
iret ; Retorna de la interrupción
5.9.11 Módulo timer
El timer es un módulo que dispone de dos registros internos:
- Registro
CONT: ubicado en la dirección10hde la memoria E/S. - Registro
COMP: ubicado en la dirección11hde la memoria E/S.
Este módulo está basado en el PIT 8253 de Intel, aunque se han realizado modificaciones para simplificar su funcionamiento. El registro COMP permite establecer el valor de comparación con CONT. Si se escribe un valor de 0 en COMP, el timer se desactiva y no genera interrupciones. Cualquier otro valor habilita el timer y permite su funcionamiento normal.
El reloj es un dispositivo que hace tic cada segundo. Al hacer tic, incrementa el registro CONT del [timer] en uno.
; Programa Imprime 'Hola' a los 10 segundos de iniciado y luego termina.
; Utiliza la interrupción del TIMER (ID = 5).
mensaje db 'Hola' ; Mensaje a imprimir
imprimio db 0 ; Flag para saber si ya imprimió
; Definición de direcciones de registros de dispositivos
CONT equ 10h ; Registro de conteo del timer
COMP equ 11h ; Registro de comparación del timer
EOI equ 20h ; End Of Interrupt (para PIC)
IMR equ 21h ; Interrupt Mask Register (PIC)
INT1 equ 25h ; Registro de vector de interrupción 1
; Habilitar interrupciones del timer
; IMR = 1111 1101b (solo habilita interrupciones del timer y teclado)
mov al, 11111101b ; Habilita interrupciones del timer (bit 1 en 0)
out IMR, al
; Configurar vector de interrupción del timer
mov al, 5 ; ID de interrupción del timer
out INT1, al ; Asigna rutina de atención a la posición 5
; Instalar rutina de interrupción en el vector
mov bl, 5 ; Vector de interrupción 5
mov [bl], imp_msj ; Apunta a la rutina imp_msj
; Configurar timer para 3 segundos
mov al, 10 ; Valor de comparación (10 segundos)
out COMP, al
mov al, 0 ; Reinicia el contador del timer
out CONT, al
; Esperar a que se imprima el mensaje
loopinf: cmp imprimio, 0 ; Ya imprimió
jz loopinf ; Si no, sigue esperando
hlt ; Termina el programa
; Rutina de interrupción del timer
org 50h
imp_msj:
mov bl, offset mensaje ; Dirección del mensaje
mov al, 4 ; Servicio de impresión
int 7 ; Llama a la interrupción de impresión
mov imprimio, 1 ; Marca que ya imprimió
mov al, 20h ; Señal de fin de interrupción
out EOI, al ; Notifica al PIC
iret ; Retorna de la interrupción