Entradas etiquetadas con Z80 Core
Simulador funcionando
4 jul
He hecho algunos pequeños retoques a la interfaz que os mostré ayer, añadido atajos de teclado, etc. pero lo más importante es que ya funciona el ciclo de emulación del Z80.
Por otro lado, a medida que iba añadiendo las funcionalidades se me iban ocurriendo otras que me están resultando bastante útiles como la de poder modificar en tiempo real la memoria del procesador para así ir ejecutando instrucciones o datos sin necesidad de escribir un programa previamente.
De momento solo tengo implementadas dos instrucciones (NOP y EX AF,AF’) para poder probar el tema, así que a partir de ahora el objetivo es ir implementando instrucciones y testeándolas en el propio simulador.
Mañana si tengo un rato os explico cómo he implementado el ciclo de emulación del Z80 para que os hagáis una idea de las opciones tomadas.
Interfaz gráfica del Simulador
3 jul
Desde mi llegada al pueblo el pasado martes (desde aquí gracias al vecino a través de cuya WIFI abierta escribo esto) he estado estudiando a fondo el funcionamiento del Z80 para ir haciéndome un esquema mental de cómo implementar su emulación. Anoche finalmente acabé un esqueleto mínimo de las clases Z80 y Memoria que probé como pude desde una interfaz de comandos.
Me he inspirado en el interfaz del Z80 Simulator IDE (una aplicación comercial para Windows), pero añadiendo algunas cosillas nuevas y situando otras más acorde a mi gusto.
Os dejo una captura de pantalla para que me digáis qué os parece

Las instrucciones del Z80
30 jun
Cada instrucción específica afectará de diferente manera a ciertos elementos del procesador (registros principalmente) o a dispositivos conectados a él (memorias y otros dispositivos de entrada/salida).
De esta manera podemos dividir las instrucciones en varios grupos como serían las de carga (mueven información entre registros o registros y memoria), aritméticas y lógicas (operan con los datos), de salto y bifurcaciones, etc.
Las instrucciones están formadas siempre por un código de operación (un prefijo que indica de qué instrucción se trata) y cero o más parámetros necesarios para la instrucción en cuestión.
En el caso del Z80, los códigos de operación de su juego de instrucciones siempre ocupan un byte, por lo tanto existen 256 códigos de operación diferentes que nos resultarían en unas teóricas 256 instrucciones diferentes. Sin embargo, esto último no es cierto; en este procesador existen ciertos códigos de operación que se usan como un prefijo, de modo que su siguiente parámetro (otro byte) se usará como un subcódigo de operación que añadirá otras 256 instrucciones diferentes.
En el Z80 tenemos 4 de estos prefijos: CB, DD, ED y FD, pero aun hay más, hay otros dos prefijos de segundo nivel que abren otras 256 posibles instrucciones cada uno: DDCB y FDCB. En definitiva, gracias a estos prefijos contamos con unas 1700 posibles instrucciones (algunos códigos de operación no se utilizan).
Cada una de estas instrucciones tiene una correspondencia en ensamblador (un código mnemónico) para facilitar la programación por parte del usuario, así la instrucción de CO (código de operación) 0×00 se corresponde con el mnemónico NOP, 0xD1 con POP DE o 0xDD94 con SUB IXh.
El procesador a la hora de ejecutar un programa no hace más que ir leyendo de las posiciones de memoria apuntadas por el PC (el registro contador de programa) y ejecutando la instrucción correspondiente al código de operación allí encontrado. ¿Sencillo, verdad?
A la hora de programar el núcleo del Z80 para el emulador la tarea más larga y cansina por lo repetitivo es la de implementar estas 1700 instrucciones, pues si bien muchas de ellas son iguales solo que cambiando los parámetros (mover cada uno de los distintos registros al acumulador, por ejemplo), aun así se quedan en unas 150 instrucciones totalmente diferentes unas de otras.
Por otro lado, para obtener una implementación más eficiente mi idea es crear una tabla con todas ellas (las 1700) a la que se salte tras analizar el CO leido.
Espero que haya quedado claro, sino ya sabéis, preguntad en los comentarios y haré lo posible para hacerlo más entendible
Al final vuelvo a las SDL…
27 jun
Por este motivo, además de mi mayor conocimiento de SDL (con el consiguiente ahorro de tiempo), he decidido “volver” a estas últimas.
La integración de SDL con QT4 en una misma ventana creo que también da problemas, así que me estoy planteando hacer un frontend al estilo del MAME en QT4 con sus menús, opciones, etc. que también contendría el listado de ROMs disponibles y desde ahí lanzar una ventana SDL con la emulación del juego seleccionado.
Tengo que refinar un poco esta última idea, pero incluso podría ser interesante separar ambas partes (interfaz y emulación) en dos aplicaciones diferentes y comunicarlas por debajo mediante sockets…
El pseudo anteproyecto
19 jun
Os lo pongo por aquí para ver si antes de dos horas que lo imprima veis que me falta algo o algún cambio que creáis necesario.
El profesor da asignaturas de compiladores, intérpretes, máquinas virtuales, etc., así que supongo que entenderá los términos de los que hablo (aunque algunos los he puesto más en lenguaje de su campo).
Si bien la versión que se presentará funcionará bajo el sistema operativo GNU/Linux y arquitectura x86, la realización de este emulador se hará con el propósito de ser fácilmente portable a otros sistemas operativos y/o arquitecturas.
Un emulador consiste en la implementación mediante software de una máquina completa, creando una máquina virtual que reproduce de la manera más fiel posible el comportamiento de cada uno de los componentes de la máquina física real.
En el caso particular de este proyecto, la máquina a emular es la videoconsola Master System de la compañía japonesa SEGA. El emulador a construir deberá reproducir el comportamiento de cada uno de los componentes de dicha videoconsola (microprocesador Zilog Z80, controlador gráfico, controlador de sonido, etc.) con el objetivo de ser capaz de ejecutar correctamente los programas (juegos) que originalmente se escribieron para la misma.
Estos programas, originalmente almacenados en una memoria ROM situada en el interior de los cartuchos en los que se distribuían, serán interpretados por el componente correspondiente al procesador Z80 (están escritos en su lenguaje máquina) el cual interactuará con el resto de componentes que forman el emulador para obtener unos resultados idénticos a los de la máquina real.
Así mismo, se incorporarán en la medida de lo posible otras características adicionales no presentes en la máquina original pero que pudieran mejorar la experiencia como filtros gráficos, reescalados de imagen, etc.
El emulador contará además con una interfaz gráfica de usuario, de modo que sea posible su configuración, carga de programas y ejecución de forma sencilla usando solamente el ratón.
Por otro lado, para depuración de los módulos incluidos se crearán las herramientas que se estimen necesarias (interfaz interactivo para el Z80, desensamblador, etc.) que también serán incluidas junto con el resto de documentación.
Arquitectura del Z80 (Parte II)
19 jun
Una interrupción es una señal por la cual se interrumpe momentáneamente el secuenciamiento del programa, se trata dicha interrupción mediante una rutina y después se vuelve al punto donde se interrumpió.
Como ya comenté previamente, en el Z80 tenemos 2 biestables (dispositivos que almacenan un bit) llamados IFF1 y IFF2. El primero se usa para indicar si las interrupciones enmascarables (las que pueden ignorarse) están habilitadas (IFF1=1) o inhibidas (IFF1=0) y el segundo nos servirá como respaldo del primero (ya veremos luego como).
Hay dos tipos de interrupciones: enmascarables (INT) y no enmascarables (NMI). La diferencia entre ambas es que las primeras pueden ignorarse si así se configura el computador y las segundas hay que tratarlas obligatoriamente.
Para habilitar las INT se usa la instrucción EI que pone a 1 los biestables IFF1 y IFF2, mientras que para inhibirlas usaremos la instrucción DI que los pone a 0.
Interrupción no enmascarable
Cuando se produce una NMI el procesador sigue los siguientes pasos:
- Salvaguarda el contador de programa (PC) en la pila.
- Guarda el valor de IFF1 en IFF2 y pone IFF1=0 (inhibe INT).
- Pone PC=0×0066 (Salta a la dirección 0×0066).
Finalmente, cuando acabe la rutina de tratamiento de interrupción se ejecutará la instrucción RETN que copia IFF2 en IFF1 (restaura el valor guardado) y restaura el PC desde la pila para continuar la ejecución donde se quedó.
Interrupción enmascarable
Hay tres modos diferentes en que puede estar el procesador para atender a las INT, estos modos llamados 0, 1 y 2 se establecen con las instrucciones IM0, IM1 y IM2.
Modo 0
Es similar al modo del 8080 de Intel, el dispositivo que genera la INT pondrá un código de operación de una instrucción en el bus de datos (normalmente una llamada a subrutina) y a partir de ahí seguirá la ejecución.
Modo 1
Es análogo al tratamiento de las NMI, solo que ahora se salta a la dirección 0×0038 y es enmascarable.
Modo 2
En este modo se usan interrupciones vectorizadas.
El dispositivo pone en el bus de datos un byte. Este byte se tratará como la parte baja de una dirección (recordemos de 16 bits) y el valor del registro I como la parte alta de dicha dirección. Finalmente, se pondrá a 0 el bit menos significativo para que sean direcciones pares.
En esta dirección se encontrará la dirección de la rutina de tratamiento de interrupción adecuada a la que se saltará (previa salvaguarda del PC en la pila).
Consideraciones
Eso sería básicamente todo el tema de las interrupciones, cabe destacar que las interrupciones no enmascarables pueden ser anidadas (una interrupción interrumpe a otra), pero no pueden saltar si se está tratando otra no enmascarable.
Finalmente, he buscado en varios libros y no he encontrado ninguna referencia a que se haga un guardado automático del registro de flags (F) en la pila a semejanza de como ocurre con el PC, así que supongo que será cuestión del programador el contemplar su salvaguarda.
Arquitectura del Z80 (Parte I)
19 jun
El Zilog Z80 es un micro catalogado a veces como de 8 bits (todavía no entiendo en base a qué se hace ese tipo de catalogaciones, pero bueno), poseyendo un bus de direcciones de 16 bits y un bus de datos de 8 bits.
Veamos un esquema explicativo:
- B, C, D, E, H, L y B’, C’, D’, H’, L’. Estos registros son de 8 bits, pero pueden agruparse por pares en registros de 16 bits de la forma BC, DE, HL y B’C', D’E', H’L’. Tienen la particularidad de que solo son accesibles a la vez 6 de ellos, pues están divididos en 2 bancos intercambiables por medio de unas instrucciones.
- A, F y A’, F’. Como en el caso anterior se trata de registros de 8 bits de los que solo dos son accesibles al mismo tiempo por estar divididos en bancos. El registro A es el acumulador que, como podemos ver en el gráfico, siempre será unos de los operandos de la ALU (Unidad Aritmético Lógica). Por su parte, en el registro F se encuentran los flags de la máquina que guardan su estado.
- PC. Contador de programa, apunta a la dirección de la siguiente instrucción a ejecutar, por lo tanto tiene que ser de 16 bits.
- SP. Puntero de pila, apunta a la primera dirección libre de la pila de ejecución, por tanto es necesario que también sea de 16 bits.
- IX e IY. Estos dos registros de 16 bits se usan como dirección base para instrucciones que hacen uso de vectores. Como podemos ver, existe una mini ALU que solo sirve para sumarle un dato de 8 bits a estos registros y volcar el resultado al bus de direcciones.
- I. Este registro de 8 bits se usa para el tratamiento de interrupciones.
- R. Este registro de 8 bits se usa como salida para refrescar memorias externas.
- Bit 7: Flag S. Es el flag que indica el signo, es decir, una copia del bit más significativo de la última operación de la ALU.
- Bit 6: Flag Z. Este flag indica si el resultado de la última operación es cero.
- Bit 5: Flag 5. Este bit en algunos documentos dicen que tiene un valor aleatorio, pero en otros he leído que guarda una copia del bit 5 del resultado de la última operación.
- Bit 4: Flag H. Guarda el acarreo del bit 3 al 4 de la operación (supongo que será útil para operaciones en BCD).
- Bit 3: Flag 3. Este bit en algunos documentos dicen que tiene un valor aleatorio, pero en otros he leído que guarda una copia del bit 3 del resultado de la última operación.
- Bit 2: Flag P/V. Dependiendo de la operación, en este bit se muestra si el resultado tiene paridad par (existe un número par de 1s en el registro) o bien hubo desbordamiento.
- Bit 1: Flag N. Se activará si la última operación fue una resta.
- Bit 0: Flag C. Es el bit de acarreo, se activará si el resultado de la operación no entra en el registro.
Una vez visto esto, cabría plantearse cómo implementarlo dentro del emulador.
Mi idea es agruparlo todo, junto a otra información de estado (como los biestables IFF1 y IFF2 que controlan las interrupciones) que vaya viendo más adelante, dentro de una misma estructura de datos. Con esta agrupación sería muy sencillo cargar o salvar el estado interno, útil para transmitir esa información a otra aplicación (como la GUI de la que hablé en la entrada anterior) o bien para los famosos savestates de las partidas.
La siguiente pregunta que me hice fue: ¿qué sería más conveniente para almacenar los registros de 8 bits que pueden agruparse en pares? ¿un tipo de 8 o 16 bits?
La respuesta es sencilla: ambos. En C existe un tipo de estructuras llamadas union en las que puedes declarar varios tipos que comparten la memoria de dicha estructura, es decir, en una estructura union de 16 bits se podría acceder a los 16 bits de golpe (por ejemplo si fuera el par DE) o bien a los 8 bits superiores (D) o los 8 bits inferiores (E).
Buscando una biblioteca gráfica
19 jun
El primer componente del emulador que quisiera programar es el núcleo del Z80, pero en lugar de programarlo del tirón me gustaría ir implementándolo poco a poco a medida que lo voy testeando. Podría ir depurándolo con gdb u otro depurador similar y ver cómo cambia el estado interno, pero he pensando en hacer una interfaz que haga uso de él y donde pueda ver el estado de cada registro, memoria, etc.
Mi primera aproximación era hacerlo en modo texto para ejecutarse via terminal, pero, pensándolo mejor, ya que implementaré una interfaz gráfica para el emulador, ¿por qué no ir cogiendo experiencia haciendo otra para depurar el Z80?
Como ya dije anteriormente, mi idea es hacer un emulador para GNU/Linux, pero con la idea de ser relativamente portable a otras plataformas, así que inicié una búsqueda de bibliotecas gráficas con estas características.
Después de unas horas mi cerco se redujo a tres candidatas: Qt4, GTK+ y wxWidgets.
Las tres parecían bastante apropiadas, pero tras consultar opiniones se me ocurrió probar Qt4 y quedé prendando.
El hecho de estar pensada para usarse con C++ era un punto a favor, pues ya comenté que voy a hacer este emulador con orientación a objetos (quizá no sea lo más óptimo, pero no hay modo mejor de coger práctica con dicha metodología que aplicándola a algo que te gusta), por otro lado es portable, tiene una documentación bastante buena, se obtienen resultados con relativo poco código y trae unas herramientas fabulosas para crear interfaces en un entorno gráfico y el soporte multilenguaje es sencillísimo.
Por otro lado, en mis primeras búsquedas estuve mirando cómo combinar estas bibliotecas junto con SDL para el tema gráfico, pero encontré algunos problemillas de incompatibilidades, así que se me ocurrió otra idea: ¿por qué no usar OpenGL? También es multiplataforma, rápido y seguro que para cosas 2D no será demasiado complicado de usar.
Echando un vistazo a los propios ejemplos de Qt4 he visto que se integra fantásticamente con OpenGL, así que en principio ya tengo seleccionado todo lo necesario para el apartado gráfico.
Bueno, lo siguiente será definir la estructura de datos para representar el estado interno del Z80 y construir el interfaz de depuración que haga uso del mismo, pero antes tengo que buscar mi última versión del GP Lady Killer para mandársela a cierta compañía extranjera…
Presentación del proyecto
19 jun
Para la gente que llegue aquí por primera vez, voy a volver a hacer una presentación de este proyecto que espero resulte interesante. Así mismo, publicaré las (pocas) entradas que dejé antes de que las prácticas y exámenes colmaran todo mi tiempo libre.
La publicación de este blog tiene un objetivo claro: mostrar los avances de mi trabajo final de carrera.
Hace ya unos cuatro meses fui a hablar con el que sería mi tutor y le propuse hacer un proyecto propio: un emulador de SEGA Master System; afortunadamente, aunque algo reacio por no entender muy bien de qué se trataba, aceptó llevármelo
¿Por qué de Master System? Bien, por dos motivos: uno sentimental, pues fue la primera consola que me regalaron (la Videopac ya estaba en casa desde que tengo uso de razón) y el segundo para empezar con un emulador no demasiado complicado para no tener que emplear dos años en su realización.
Alguno podrá decir que ya tengo mi AlexKidd2X (mi emulador de Master System/Game Gear para la consola portatil GP2X), pero si bien he metido mucho código en él, la mayor parte del código de los chips que integra está hecho por otras personas y mi deseo es hacer uno desde cero (from scratch que dirían los guiris) para aprender todo lo posible.
Así mismo, mi idea es hacerlo para PC y SO GNU/Linux (aunque con la idea de que sea portable) y usarlo por otro modo para aprender y afianzar mis conocimientos en otros aspectos como C++, interfaces gráficas, etc. que me puedan ser útiles para un futuro porque, ¿qué mejor manera de aprender nuevas técnicas que hacerlo aplicándolas a algo que te gusta?
No creo que publique mucho código hasta su finalización (si las cosas no se tuercen estará licenciado con una GPL), pero creo que mis experiencias pueden ser de ayuda para la gente que quiera iniciarse en este mundillo o, simplemente, gente con curiosidad por estos temas.
Finalmente, el nombre código de este emulador será gSMS de GPL SMS, ¿original, eh? :p


