Emulador WOPR por Jesús Arias. ---------------------------------------------------------------------------- Introducción Histórica: La WOPR es un prototipo de microprocesador de caracter educacional basado en el popular Z80. Este prototipo fue diseñado por primera vez en 1987 con el condicionante de reducir al mínimo el número de componentes y los costes, en clara contraposición con el ordenador de la película "Juegos de Guerra" de la que tomó prestadas las siglas :) La idea original de la WOPR constaba de las siguientes partes: CPU: Z80 (4MHz) Memoria: 1Kb ROM + + 1Kb RAM (HY6116) E/S: 8255 PPI Con el fin de reducir la complejidad del circuito y evitar la grabación de EPROM la memoria ROM en realidad sería una parte de la RAM, que no podría ser escrita desde el microprocesador, y que mantendría su contenido gracias a una batería. En cuanto a la entrada-salida el integrado previsto fue sustituido por el Z80-PIO que nos resultó más barato y fácil de conseguir. Conectado al PIO se encontraba un terminal compuesto por una pantalla de 6 digitos de 7 segmentos y un teclado de 24 teclas. Tanto la pantalla como el teclado tienen un barrido secuencial controlado por un contador 4017. Los 7 segmentos de los dígitos, junto con el punto decimal se activan desde el puerto A del PIO, mientras que las 4 filas del teclado entran a los bits menos significativos del puerto B. El barrido del terminal se sincroniza con la CPU gracias a una línea de Reset y otra de Reloj, que salen de los bits más significativos del puerto B. La versión original de la WOPR fue desarrollada y llevada a la práctica por Jesús Arias, Alberto Vegas y Raul de Frutos, estudiantes de Físicas en aquel entonces. La grabación de la pseudo-ROM se realizó a través del puerto paralelo de un ordenador MSX Spectravideo. El ensamblador utilizado para generar el programa monitor estaba escrito en Basic. El método empleado para construir el circuito de la WOPR consistía en insertar los integrados en una placa de plástico aislante y unir sus patas mediante hilos de cobre que se enrollaban en los pines. La WOPR fue sufriendo variaciones con el paso del tiempo, que la apartaron cada vez más de la idea original. La primera de ellas consistió en la soldadura de todos los contactos en una placa de islas. Se incorporaron zócalos para la expansión de la RAM, y la ROM se instaló en un integrado aparte, aunque seguía siendo una RAM CMOS con batería. La memoria llegó a alcanzar en total 8 KB. Se incluyó una interrupción periódica con una frecuencia de 512 Hz que se empleó en el barrido del terminal y en la actualización de la hora. El oscilador de reloj pasó de ser de relajación a controlado por cuarzo. Se utilizó sólo lógica CMOS en la decodificación de direcciónes, quedando totalmente decodificados los 64 Kb del espacio de memoria del Z80. Las expansiones mencionadas se tadujeron en una creciente complejidad de la maraña de cables que constituía el circuito y este acabó fallando. Me planteé entonces construir una versión en circuito impreso que reprodujese la idea original del prototipo. La única modificación respecto de este planteamiento ha consistido en dejar que el barrido del terminal se lleve a cabo libremente, mediante un oscilador externo (Sí, Luis, un 555 :). La CPU se sincroniza con el barrido monitorizando el estado del contador y su señal de reloj. ---------------------------------------------------------------------------- El Emulador de WOPR: Este emulador es una implementación casi directa del emulador portable de Z80 de Marat Fayzullin. Tan sólo ha sido necesario implementar las funciones M_RDMEM(), M_WRMEM(), DoIn, y DoOut. Las dos últimas han sido las más complicadas, pues son las relacionadas con la entrada-salida. Para proporcionar al emulador un aspecto que recordase la versión hardware, he utilizado la librería Xforms, junto con parte de código reciclado de un programa que implementaba una pantalla de 7 segmentos en X-windows. Por último he incluido la posibilidad de incluir código o datos en la memoria antes de arrancar la emulación de la CPU, lo que facilita la ejecución de programas generados con ensambladores cruzados. En el emulador se han reproducido las limitaciones de memoria del prototipo, pero pueden cambiarse fácilmente editando las definiciones asociadas a la memoria en el comienzo del fichero "wopr.c". La emulación del PPI 8255 es realmente pobre, por no decir inexistente, limitandose a la simulación de un puerto de salida en la dirección 0 y a uno de entrada en la dirección 1. Podría pensarse en elaborar una emulación más realista, pero si tenemos en cuenta que en el prototipo el puerto C no se usa, y que el puerto A y el B deben ser entrada y salida respectivamente para que funcione el terminal, no parce que merezca la pena una emulación mejor. El emulador al arrancar intenta cargar en la ROM el contenido del fichero "wopr.rom" abortándose la ejecuación si no se encuentra. Cambiando el contenido de este fichero puede simularse la actualización de la ROM. Los programas se ejecutarán habitualmente en la RAM. Para cargar un programa basta indicarlo en la línea de comandos. El emulador reconoce automáticamente y carga los ficheros hexadecimales en formato Intel, Mostek y Motorola S-record. También pueden cargarse imágenes binarias, pero en este caso debe indicarse cual es su dirección de comienzo en la memoria. Por ejemplo, para cargar el programa de ejemplo prim.s19, basta con ejecutar: wopr prim.s19 El emulador devuelve el mensaje: File Loaded :prim.s19 . Format :Motorola S-record . Base :0x416 Si en su lugar quisiesemos cargar una versión binaria del mismo código el comando empleado sería: wopr -b 0x416 prim.bin El emulador devuelve ahora: Binary File Loaded :prim.bin at Base :0x416 ----------------------------------------------------------------------------- La intefaz de la WOPR: La WOPR incorpora un terminal muy simple pensado para la introdución de código hexadecimal en la RAM, su monitorizacion y ejecución. Cuando el control del programa se tiene en el monitor de la ROM en la pantalla se muestra en haxadecimal la dirección actual de memoria y su contenido, separados por un punto decimal. Para cambiar el contenido de esta posición basta con teclear un número hexadecimal de dos dígitos. Por cada pulsación se añade un dígito hexadecimal por la derecha y el dato se desplaza a la izquierda. de este modo no se necesita una tecla de "Enter". La dirección actual de memoria se puede cambiar mediante las teclas "+","-" y "Go". Con las primeas se puede incrementar o decrementar por cada pulsación. Si se pulsa la tecla "GO" la WOPR nos pregunta explicitamente la nueva dirección de memoria actual. Para terminar de introducir la nueva dirección se debe pulsar cualquier tecla no numérica. Ejemplo de cambio de dirección actual: Pantalla: 0416.00 Pulsamos (Go) -0416- Pulsamos (0) -4160- Pulsamos (0) -1600- Pulsamos (0) -6000- Pulsamos (0) -0000- Pulsamos (Go,Exe,+,-,F1,F2,F3 o F4) 0000.F3 Para ejecutar código a partir de la dirección actual se pulsa la tecla Exe, y se encomienda uno a los espíritus para que el programa funcione bien :) Las teclas F1,F2,F3,F4, sobraban en un principio, y se les buscó una aplicación como vectores de ejecución de rutinas, de modo que no sea necesario teclear la dirección y la tecla de ejecución. Basta en este caso con teclear Fx para lanzar la rutina. Los vectores asociados a estas teclas se tienen en las direcciones: F1 -> 400h F2 -> 402h F3 -> 404h F4 -> 406h Estos vectores se cargan con los valores por defecto si se mantiene pulsada la tecla F1 durante el encendido de la WOPR. En este caso la tecla F1 apunta a una rutina de "About WOPR" y la dirección actual pasa a la 416h, la primera dirección de RAM no ocupada. ----------------------------------------------------------------------------- Desarrollo de programas: Para este propósito es muy recomendable el uso de las rutinas de E/S de la ROM, cuyas funciones están comentados en el código fuente de la misma. Además las posiciones de RAM 408h-40Dh contienen un bit por cada segmento de la pantalla, y las direcciones 40Eh-415h guardan el estado de las teclas tras la ejecución de la rutina "scan". El código fuente de la ROM se encuentra en los ficheros rom.asm y rom.lst ----------------------------------------------------------------------------- Versiones 2.x: Ventana de DEPURACION: En las versiones del emulador 2.x se incluye una ventana con un depurador interactivo. Este depurador nos permite conocer el estado de los registros del Z80, y seguir la ejecución paso a paso de los programas de código máquina que ejecuta el emulador. En la ventana de depuración aparecen varios campos: - Dissassembled @PC - Stack Trace - Memory - Z80 Registers - Botones de control y Breakpoint. Al arrancar el emulador el estado de depuración es "running" y los campos aparacen en blanco. Pulsando el boton "STOP" la ejecución del emulador se detiene y entonces se muestra el contenido de los campos citados. En la ventana "Dissassembled @PC" se muestra una porción del código ensamblador que está ejecutando el emulador, lo que puede ser muy útil a la hora de seguir la ejecución de un programa. El contenido de los registros del Z80 puede cambiarse tecleando en ellos números hexadecimales. Los Flags pueden ponerse a 1 y a 0 individualmente mediante el ratón. El contenido del acumulador se muestra también en binario, y los registros o pares de registros que pueden realizar funciones de puntero muestran también el valor del dato al que apuntan. Pulsando el botón con forma de flecha que acompaña al registro puntero se muestra en la ventana de memoria un área de datos más amplia. La ventana "Stack-Trace" muestra los últimos datos (de 16 bits) que se han introducido en la pila. Los botones de control permiten la ejecución controlada de los programas. Además del botón "STOP", ya comentado, tenemos otros 4, que son: "STEP","NEXT","TRACE" y "RUN". Pulsando el botón STEP el emulador ejecuta exactamente una instrucción del Z80, se actualiza toda la ventana de depuración y se detiene la ejecución hasta que se pulse de nuevo un botón de control. Si se pulsa el botón NEXT el emulador continúa la ejecución hasta llegar a la instrucción que sigue a la actual. Esto es particularmente interesante cuando se quiere ejecutar de un sólo paso toda una subrutina o un bucle. Si la ejecución no llega nunca a la instrucción mencionada el efecto será el mismo que el de pulsar el botón RUN. Pulsando el boton TRACE el emulador ejecuta las instruciones una tras otra, pero actualizando la ventana de depuración tras cada instrucción, por lo que la velocidad de ejecución se queda en unas pocas instrucciones por segundo. Por último, pulsando la tecla RUN continúa la ejecución normal de los programas sin que se actualice la ventana de depuración. En este estado la ventana "Dissassembled @PC" se encuentra borrada, y no se pude alterar el contenido de los registros. La ejecución en modo RUN puede detenerse automáticamente, si en un momento dado se alcanza la dirección de código indicada en el campo "Brk" (Breakpoint). En este depurador se dispone de un único "breakpoint" que se encuentra desactivado por defecto, de modo que no afecta a la ejecución de los programas. Para activarlo se puede teclear en el campo "Brk" la direción de memoria en la que se quiere detener la ejecución. Otra forma de activar el "breakpoint" consiste en pulsar el ratón sobre una de las instrucciones mostradas en la ventana "Dissassembled @PC" cuando la ejecución se encuentra detenida. Para desactivar el "breakpoint" se debe pulsar el botón "C", que se encuentra al lado del campo "Brk". ----------------------------------------------------------------------------- ¡¡ Qué disfrutes este emulador !! Sugerencias y críticas en: jesus@ele.cie.uva.es