Mantengámonos en la duda, seremos más sabios

Siempre me ha resultado bastante complicado afirmar algo con rotundidad, y no por falta de confianza, sino por declararme ignorante y porque considero que hay alguién mas inteligente que yo que podría dar una mejor aproximación, por eso en mis conversaciones uso frecuentemente frases del tipo "Personalmente opino que...", "Bajo mi punto de vista.." "Yo creo que..."

También me resulta extraño conocer a personas que se sienten con la autoridad para afirmar. Bajo mi punto de vista la afirmación implica en muchos aspectos ignorancia . A groso modo una persona que afirma es una persona que bajo su punto de vista sabe y por consiguiente no necesita aprender más.

En la historia humana, durante muchos siglos, hemos vivido en la ignorancia y lejos de aceptarla y combatirla hemos mirado para otro lado y nos hemos declarado sabios. ¿Por qué desde hace 300-400 años estamos creciendo a pasos agigantados en cuanto a ciencia se refiere? La respuesta es que alrededor del año 1500 d.c se produjo lo que los historiadores conocen como La revolución científica. ¿Como se produjo ese revolución? Uno de los detonantes de dicha revolución fue Cristobal Colón al descubrir América. El descubrimiento de América produjo un cambio en la mentalidad de Europa, significaba que no todo estaba hecho, que había por hacer todavía muchas cosas. Colón no era científico pero sentó el cambio de mentalidad, a Colón le siguieron científicos de verdad como Isaac Newton o Galileo Galilei, o unos estadistas que fundaron el primer fondo de pensiones en irlanda, que se apoyaba en la estadística para mantener la estabilidad del sistema.

En resumidas cuentas, la revolución científica se dió porque aceptamos nuestra ignorancia, y solamente si aceptamos nuestra ignorancia podemos seguir aprendiendo. En épocas anteriores el que quería saber algo debía consultar al sabio local y este basaba su respuesta en su experiencia o en Escrituras Sagradas, que a menudo, si el dilema no se encontraba en dichas escrituras se entendía que ese problema no era importante, porque si fuese importante Dios lo hubiese contemplado en sus escrituras (o a través de sus profetas)

A modo de ejemplo ilustrativo voy a contar una anécdota que me pasó hace unos días.

Hace unos días, tuve un problema con un sistema de comunicacion sobre internet y decidí pedir consejo en StackOverflow. Basicamente el problema que tenía es que experimenté lentitud con la librería de comunicación volley de Android, haciendo pruebas comprobé que para acceder a un json de 0,27 KB (unos 300 Bytes) volley empleaba de media un 1000 ms. Un tiempo inaceptable para tal cantidad de información. Los tiempos eran parecidos tanto por WiFi como por 3G, sin embargo desde las herramientas de desarrollador de firefox comprobé que éste únicamente tardaba 150 ms.

Un detalle a puntualizar es que el servicio REST funciona sobre SSL y usa compresión GZIP, esto fue en la primera causa del problema que pensé, así que me dirijí a StackOverflow para exponer mi caso proporcionando las pruebas obtenidas y preguntar si había alguna forma de optimizar volley.

El primer comentario que obtuve fue de CommonsWare que es bastante importante en StackOverflow y una de las eminencias en Android. Su comentario fue el siguiente

You are comparing apples with oranges. The network tab in Firefox reports the network access time. The time between millis and System.currentTimeMillis() in your code is for both network access time and JSON parsing time.

Y su comentario es cierto, pero yo eso ya lo sabía. Lo que yo quería saber es si ese tiempo de procesamiento de json o el tiempo de acceso a la red eran normales para un json de solo 300 bytes ya que intuía que no era así. Su siguiente respuesta a mi contestanción sobre si ese tiempo de procesamiento json era razonable fue la siguiente

"json parsing could take that time?" -- sure. You might run a test where you use JSONObject to parse a static String containing a typical HTTP response payload, to see how long it takes in the same environment

Como no tenía nada que perder hice los cambios perninentes en el código para recuperar un string sin procesar y procesarla yo, en vez de obtener un objeto JSON de primeras. Con este cambio hecho hice algunas pruebas y esto son algunos de los resultados:

D/Before process:1132
D/After process:1133
D/Before process:918
D/After process:920

Aquí muestro 2 pruebas que hice, calculé el tiempo justo cuando volley recuperaba el contenido como tal y después de haber convertido el String a un JSONObject. Como se puede apreciar, en la primera prueba este procesamiento json solo llevo 1 ms y en el segundo caso 2 ms. Tiempos más que aceptables según mi experiencia y teniendo en cuenta el tamaño del json. Las pruebas que hice antes de usar Strings son las siguientes:

D/Before process:1041
D/After process:1045
D/Before process:907
D/After process:908

En estas pruebas usé la misma terminología, pero se refieren a cosas distintas, en este caso ya no recibimos un String sino un JSONObject, por tanto el tiempo medido en Before process simboliza el tiempo que volley ha empleado en acceder a la red y en transformar el string en un JSONObject, por tanto aquí Before process es el After process de las pruebas anteriores. En cambio en estas pruebas After process se refiere a tiempo que ha transcurrido desde que se ha hecho la petición hasta que el JSON ha sido parseado, es decir, hasta que se han extraido los datos de interes del json, por tanto lo que no interesa en este caso es el Before process y como podemos observar es similiar a la prueba hecha con strings, de hecho es incluso más rápido usar el método que nos devuelve un JSONObject en vez de un string, a diferencia de lo que había pronosticado CommonsWare.

Respondí con las pruebas realizadas y ya no obtuve más respuesta.

El problema ya está solucionado y como supuse inicialmente era una fallo de la gestión de la capa SSL, un fallo generado por mi mismo, que fuí el que programó dicha capa.

Lo que me sorprendió fue la tajante respuesta de CommonsWare como si tuviese la verdad absoluta, esto no es una crítica a CommonsWare ni mucho menos, solamente es un ejemplo que me ha servido de lo que quería expresar, que una persona bastante competente y con bastantes conocimientos.

P.D Tras hacer los cambios pertinentes en la capa de seguridad y criptografía SSL obtengo unos tiempos de entre 140 y 300 ms, tiempos más que aceptables.

MissingNo, el pokemon fallo

Introducción

Parece que ahora mucha gente se ha enganchado a la fiebre Pokemon través de Pokemon Go pero hubo vida antes de Pokemon Go y desde mi punto de vista fue mejor esa vida, me refiero a los juegos de GameBoy, GameBoy Color, GameBoy Advance, Nintendo DS y Nintendo 3DS.

En este artículo me gustaría hablar de los primeros juegos, los de la primera generación, pokemon rojo, pokemon azul y pokemon amarillo.

El primer juego al que jugué fue pokemon azul, en casa de un amigo, en aquél momento no me llamó mucho la atención, eso sería por el año 96/97, poco después empezaría la fiebre pokemon y recuerdo que yo (que no tenía aún la GameBoy) me compré primero el pokemon amarillo antes siquiera de tener la GameBoy, meses después conseguí que mis padres me compraran una GameBoy Color.

Aquellos años se vivió una especie de histeria colectiva hacia pokemon, la mayoría de niños jugaban a pokemon, en mi ámbito casi todos mis amigos jugaban a pokemon, hacíamos peleas y cambiábamos pokemons a través del gamelink.

Entre yo y mi hermana llegamos a tener todos los juegos de pokemon hasta la tercera generación, excepto uno, esto es, Pokemon Rojo, Pokemon Azul, Pokemon Amarillo, Pokemon Oro, Pokemon Plata, Pokemon Cristal, Pokemon Rubi y Pokemon Zafiro (el juego que faltó fue Pokemon Esmeralda, de la tercera generación).

Al igual que muchos de los que empezamos con los juegos considero que los mejores juegos de pokemon han sido Pokemon Oro/Plata/Cristal, con un total de 251 pokemons que capturar y 16 medallas que conseguir.

Pero este artículo no va a tratar sobre mi infancia y pokemon o sobre la historia de pokemon en mi ámbito cercano, este artículo es un artículo técnico sobre uno de los mayores misterios de pokemon.

A mi juicio una de las causas por las que la primera y segunda generación fueron las mejores fue el hecho de que no había nada escrito sobre ellas y todo era posible. Me explico, en aquella época no nos resultaba sencillo acceder a guías precisas sobre el juego e Internet aún estaba en pañales en España por lo que pokemon era un mundo inexplorado. Hoy en día en pokemon está todo controlado, accedes a cualquier guía sobre pokemon en Internet y te dicen la verdad.

En la primera y segunda generación había una serie de mitos y leyendas que eran difíciles de comprobar a no ser que comprobases tú mismo. Alguno de los mas conocidos:

  •  En la primera generación en el SS Anne hay un camión que si lo empujas con un Nidoking a nivel 100 al que le hayas enseñado Fuerza encontraras a Mew, el pokemon legendario 151.
  • En la primera generación si entrenas un Chansey hasta nivel 99 y luego lo dejas en la guardería, al subir al nivel 100 se muere pero te deja un huevo que resulta ser togepi (pokemon de la segunda generación)
  • En la primera generación se puede acceder a una especie de jardín que hay detrás del laboratorio de Bill, llamado el valle de togepi, donde se puede encontrar al pokemon legendario 151, Mew, y a togep (pokemon de la segunda generación)
  • En la segunda generación si conseguías todos los pokemon (250) e ibas al altar del bosque, te aparecía el guardián del bosque, el pokemon legendario 251, Celebi.
  • En primera (y creo que en segunda) generación si a mitad de un intercambio pokemon apagabas la consola, el pokemon intercambiado se clonaba.
  • En la primera generación, si ibas a la zona safari, y hacías cierto ritual (no lo recuerdo muy bien, pero era algo así como guardar la partida, y apagar justo cuando estuviese guardando, intentar salir y darle a no al salir, volver a guardar la partida y apagar y salir de la zona safari) podías acceder a un lugar conocido como las islas fallo.
  • En estas islas fallo se podía encontrar al pokemon legendario 151, Mew.
  • En primera y segunda generación si hacías cierto ritual podías conseguir a un pokemon llamado Missigno que ocupaba el número 0 en la pokedex.

Habías bastantes más, pero corto aquí para no hacerlo más pesado. La mayoría de los mitos anteriores eran producto de la ficción colectiva o simples engaños (o al menos no se ha podido comprobar su veracidad). El mito de celebi por ejemplo es cierto, pero solo en la versión japonesa del juego.

El mito de las islas fallo y de MissingNo si que son ciertos, yo personalmente experimenté la entrada a las islas fallo en pleno auge de la fiebre pokemon. Aunque el mito de MissingNo no pude comprobarlo en aquella época, lo comprobé años después.

Da la casualidad que aquello eran ciertos son simples errores de programación por parte de Nintendo.

Esto que acabo de comentar, desde mi punto de vista le daba un halo de misterio a pokemon y la sensación de que todavía había cosas por descubrir. Hoy día con una simple búsqueda en Internet puedes decidir si merece la pena hacer el esfuerzo de subir un Nidoking a nivel 100 para conseguir a Mew o no.

MissingNo, El pokemon fallo

Como he dicho anteriormente esto es un artículo técnico y aunque ya he hablado sobre MissingNo en alguna que otra entrada de este blog esta vez me gustaría hablar sobre el asunto con un poco más de profundidad.

Para llevar acabo este artículo he usado el emulador VirtualBoy Advance para windows, la rom de Pokemon Rojo en inglés (el idioma debería ser indiferente) y un editor hexadecimal, en este caso HxD.

Antes de nada me gustaría mostrar el pokemon al que vamos a estudiar

012

Este pokemon es una casualidad de errores en cadena por parte de Nintendo. El simple hecho de ver a este pokemon hace que el objeto que lleves en 6 posición se incremente en su número. Lo primero que hay que saber es queMissingNo no es un pokemon, no está programador para salir en el juego, es simplemente un fallo.

Empecemos a explicar este fallo tan famoso, posiblemente el fallo más famoso de los videojuegos.

En la primera generación había 151 pokemons que capturar y se numeraban de 1 a 151, siendo 1 bulbasaur y 151 mew. Esta es la numeración que todo el mundo conoce, el orden que muestra la pokedex, pero lo cierto es que los pokemon internamente tienen otro índice distinto que va desde 1 a 190, dentro de estos 190 pokemon hay 39 MissingNo. Esta numeración parece que corresponde al orden en que se diseñaron los pokemon, esto pasa en todas las generaciones de pokemon (no solo en la primera).

¿A que corresponden estos 39 MissingNo? Pues corresponden a 39 pokemons que se diseñaron para salir en la primera generación pero que al final salieron en la segunda generación, un ejemplo es el legendario de la segunda generación Ho-oh que estaba programado para salir en la primera generación (de hecho en la serie anime sale en la primera generación) pero que se decidió dejar para la segunda generación.

En el offset de la ROM 0x41024 y hasta el offset 0x410E1 se puede observar una tabla donde mapea el índice interno con el número en la pokedex. Por ejemplo, el índice interno corresponde a al pokemon 0x70 (112) que es Rhydon, el siguiente, el índice interno 2 corresponde al pokemon 0x73 (115) que es Kangaskhan. En la siguiente imagen podemos observar esto.

001

Pero si nos damos cuenta en algunos offset observamos que apuntan al pokemon 0, esos huecos corresponden a pokemons que finalmente no salieron en la primera generación y los programadores rellenaron con 0.

Internamente el juego no maneja los números que nosotros vemos en la pokedex, maneja estos números, y esto lo veremos a continuación.

En pokemon rojo/azul hay 2 zonas en las que pueden salir pokemons, las zonas de hierba y las zonas de agua. El juego guarda en la ram, la probabilidad de batalla, y los pokemons, junto a sus niveles, que deben aparecer en cada zona del juego. Los datos en memoria están diseñados de la siguiente forma

  • 1 byte indica la probabilidad de encuentro (0 para las ciudades, ya que no hay batallas en las ciudades)
  • A continuación hay 2 bytes, 1 para el nivel del pokemon y otro para el pokemon en cuestión (este ultimo byte no contiene los pokemons en su numeración de pokedex sino mediante su numero interno). Esto se repite 10 veces, es decir, cada zona tiene hueco para 10 pokemons salvajes

Esto anterior se aplica a 2 zonas de memoria, la zona de memoria para pokemons de hierba que empieza en la dirección de memoria 0xD887 y tiene un tamaño de 21 bytes (1 byte para la frecuencia de los combates y 20 bytes para almacenar 10 pokemons y sus respectivos niveles) y la zona de memoria para pokemons de agua que empieza en la dirección de memoria 0xD8A4 y que igualmente tiene un tamaño de 21 bytes por lo explicado anteriormente.

Veamos un ejemplo, vamos a la primera ruta del juego donde se pueden encontrar pokemons:

002

Andamos un poco por la hierba y nos aparece lo siguiente:

004

Como estamos en hierba vamos a la dirección de memoria 0xD887 y vemos que la frecuencia de combate es 0x19 y vemos lo siguiente

003

Lo que está resaltado es el pokemon que nos ha salido, 0x02 es el nivel y 0xA5 es el pokemon referenciado mediante el número interno, 0xA5 es un rattata, por tanto ahí podemos observar que ha aparecido un rattata con nivel 2. Si nos metemos ahora a pueblo paleta

006

si miramos ahora la memoria veremos lo siguiente

005

Vemos que la frecuencia de combate ha cambiado a 0, es decir, no van a haber peleas, pero podemos observar algo también muy interesante, los datos de los pokemons no se han borrado, siguen ahí. El funcionamiento de todo esto es el siguiente, cuando entras en una zona de hierba o de agua (o de hierba y agua) se actualizan los datos de los pokemons que aparecen en la memoria RAM, y SOLO se vuelven a sobrescribir cuando se entra en otra zona de hierba, de agua (o de hierba y agua), cuando entras en una ciudad lo único que ocurre es que la frecuencia de combate se cambia a cero pero los datos de los pokemons siguen ahí.

Vale, ¿Pero que tiene que ver todo esto con MissingNo? De por si no tiene nada que ver, pero hay 2 errores de programación (o despistes) que hacen que esto sea una información muy valiosa.

El primer fallo tiene que ver con la actualización de la frecuencia de combate en las zonas de agua. La frecuencia de combate cambia cada vez que entramos en un nuevo mapa (tanto de hierba como de agua), pero hay una excepción, cuando en Isla Canela se utiliza surf, la frecuencia de combate de la zona de agua se actualiza, cuando esto ocurre al surfear por la orilla el juego detectará que no deben aparecer pokemons, pero la RAM contiene una frecuencia de combate distinta de 0, hay una contradicción entre lo que dice la RAM y lo que dice la ROM y por defecto aparecerán combates con los pokemons y los niveles que aparezcan en la zona de memoria de los combates de hierba, veamos un ejemplo. Nos vamos a ir a la primera ruta donde se puede combatir, donde nos ha salido el rattata nivel 2 (que habrá pokemons con niveles muy bajos), a continuación vamos a usar vuelo y volar a Isla Canela en este momento la RAM contendrá los datos de los pokemons de hierba de la ruta anterior, pero con una frecuencia de combate de 0, en cuanto hagamos surf, la frecuencia de la zona de agua se actualizará y empezaremos a tener combates con pokemons que aparecen en la ruta anteriormente mencionada, en vez de los pokemons típicos de esta ruta (que deberían tener un nivel bastante elevado, ya que Isla Canela es una de las últimas ciudades)

008

 

Ya estamos más cerca de llegar a MisingNo. ¿Que pasa si pudiésemos modificar los datos de los pokemons que aparecen en las zonas de hierba? Pues que podríamos tener cualquier pokemon que quisiésemos a cualquier nivel.

En un emulador es relativamente sencillo modificar la memoria RAM para modificar estos valores, pero MissingNo se podía ver y conseguir en los cartuchos originales de GameBoy y en estos cartuchos no es sencillo modificar la RAM (por no decir imposible, a no ser que tengas hardware específico).

Aquí es donde entra en juego el siguiente fallo o más bien despiste, hay un momento en el juego en el que se sobrescribe la zona de memoria de la RAM donde se guarda la información de los pokemons que aparecen en la zona de hierba. ¿Donde ocurre exactamente esto? Pues ocurre en Ciudad Verde. En ciudad verde hay un señor que nos enseña como capturar pokemons, en ese momento el juego necesita mostrar el nombre de este señor por tanto tiene que sobrescribir nuestro nombre, y para ello hace una copia de seguridad de nuestro nombre a otra zona de memoria. Esta zona de memoria da la casualidad que corresponde a la zona de memoria donde se encuentran los pokemons que deben aparecer en la zona de hierba.

El nombre de nuestro jugador está almacenado en la dirección de RAM 0xD158 en una codificación distinta a la ascii estándar, Esto se puede ver a continuación (El nombre de mi jugador es ASH)

009

En cuanto el señor de ciudad verde nos enseña a capturar pokemons podemos ver que estos datos (que corresponden a nuestro nombre) se han copiado a la dirección de RAM 0xD887, para posteriormente, cuando acabe este tutorial volver a copiar estos datos a la dirección RAM 0xD158.

010

En teoría esto debería ser seguro, en cuanto cambiemos de mapa estos datos serian actualizados con los pokemons que aparecen en ese mapa, pero esto se une a lo comentado anteriormente, si después del tutorial volamos directamente a Isla Canela veremos lo siguiente en esa dirección de memoria RAM

011

Podemos ver que al entrar a una zona sin combates se han puesto a 0 la frecuencia de combate de la zona de hierba y la zona de agua (esta última ya estaba a cero antes) pero como se ha dicho anteriormente los pokemons que aparecen no han sido modificados, solamente la frecuencia de combates, por tanto ahora en la zona de combates de hierba seguimos teniendo parte de nuestro nombre y seguramente equivale a pokemons que no existen y a niveles que no tienen sentido. Solo nos queda hacer surf en la orilla de Isla Canela para que se actualice la frecuencia de combate de la zona de agua y se produzcan combates con los datos de los pokemons de la zona de hierba, con el nombre ASH me han aparecido los siguientes pokemons

012013  014 015016

Si vemos la captura de memoria RAM que he puesto anteriormente vamos a intentar identificar cada una de las batallas con los bytes mostrados, la manera mas sencilla es fijarnos en los niveles.

Empezamos por Snorlax que está en nivel 141, si lo pasamos a hexadecimal tenemos tenemos 0x8D, justo en la segunda linea del dump de la RAM tenemos 8D 84 (8D es el nivel y 84 es el índice oculto que se usa para identificar a los pokemons).

Seguimos con el MissingNo nivel 80 que en hexadecimal es 0x50, en la primera línea del dump podemos ver 50 89 (50 es el nivel y 89 es el índice interno de este MissingNo).

El siguiente MissingNo está en nivel 138 que en hexadecimal es 0x8A, en la primera línea vemos también que aparece 8A 50 (8A es el nivel y 50 es el índice interno de este MissingNo).

El siguiente pokemon es un Golbat nivel 128 que en hexadecimal es 0x80, en la primera línea vemos también 80 82 (80 es el nivel y 82 es el índice interno de Golbat).

Por último tenemos otro MissingNo nivel 146 que en hexadecimal es 0x92 y si vemos en la primera línea nuevamente, justo después de la frecuencia de combate (señalada con un cuadro rojo) vemos 92 87 (92 es el nivel y 87 el índice de este MissingNo).

Como última curiosidad decir, aunque se intuía ya en la tabla de índices internos de pokemons, que hay varios MissingNo's y que no son el mismo MissingNo porque cada uno corresponde a un pokemon que en principio debería estar en la primera generación pero que finalmente no salió. Con el nombre de ASH han aparecido los siguientes MissingNo's (numero en hexadecimal)

  • 89 que corresponde al pokemon de segunda generación Smeargle
  • 50 que corresponde al pokemon de segunda generación Remoraid
  • 87 que corresponde al pokemon de segunda generación Stantler

El artículo se ha hecho demasiado largo pero aún quedan bastantes cosas que explicar como la multiplicación del 6º objeto y otros pequeños detalles que dejaré para otro artículo, pero la mayoría de esos efectos secundarios son debidos a simples buffer overflows.

Conforme vas aprendiendo más cosas sobre el mundo, dejas de creer en la magia y empiezas a pensar que todo tiene explicación. Y MissingNo no es una excepción, todo halo de romanticismo al rededor de este supuesto pokemon ha sido ya despejado con esta explicación.

Emulación de una CPU

Hacia ya tiempo que tenía en mente desarrollar un procesador por software, de hecho los primeros ficheros que tengo sobre este proyecto y la documentación datan de 2009-2010, pero debido a que es un proyecto al que hay que dedicarle bastante tiempo lo fui dejando de lado.

Este año me propuse ir desarrollandolo poco a poco a modo de pasatiempo en mis vacaciones, pero al final el ansia por ver almenos algo básico funcionando ha conseguido que me ponga a desarrollarlo antes de tiempo.

En esta entrada voy a enseñar algunas características y explicar algunos aspectos del proyecto.

Para empezar, el procesador no es mi objetivo, mi objetivo es un poco más ambicioso, es desarrollar un sistema completo con RAM, ROM, CPU y GPU.

La CPU, que es lo que viene al caso en esta entrada, es de 8 bits, capaz de direccionar hasta 16KB de memoria RAM que y tiene un juego de instrucciones de 512 instrucciones que se dividen en 256 instrucciones base y 256 instrucciones extendidas. Actualmente tengo implementadas las 256 instrucciones del juego base.

El procesador consta de 7 registros básicos de 8 bits más uno de flags. Estos registros se pueden unir para formar registros de 16 bits. A parte de estos registros tiene otros 2 de uso específico, el PC (Program Counter) y el SP (Stack Pointer) de 16 bits cada uno.

La fase de busqueda, decodificación y ejecución no tienen mucho misterio

bOps es un array de punteros a funcion.

En principio quería avanzar en la CPU propiamente dicha pero conforme iba terminando me di cuenta que necesitaría también un módulo debug (al estilo gdb) para poder probar a fondo el funcionamiento de la CPU.

Para poder hacer pruebas he tenido que crear una memoria con un esquema que no es el que yo quería pero para comprobar el correcto funcionamiento de la CPU es suficiente, esta memoria es de 1KB y se divide de la siguiente forma:

  • 0x00 - 0xFF (256 bytes): En estas posiciones se encuentra el bootloader, aunque ocupa bastante menos que 256 bytes.
  • 0x100 - 0x1FF (256 bytes): Memoria para código. Tras arrancar la CPU el bootloader salta a la posición 0x100 donde se carga el código de programa.
  • 0x200 - 0x3FF (512 bytes): Memoria RAM. Dentro de la memoria RAM cabe destacar la pila que es inicializada a 0x3FF y que va decreciendo conforme se van introduciendo datos en la misma.

Con todo esto ya estaban todos los elementos "hardware", faltan los elementos software, esto es el bootloader y un programa para testear el funcionamiento de la CPU.

No tengo compilador ni traductor para la CPU así que en vez de programar en ensamblador, he tenido que programarlo directamente en código máquina.

El bootloader simplemente inicializar una serie de registros y prepara al procesador para dar el salto a la memoria de código.

El bootloader se establece directamente en la creación de la memoria.

Con el bootloader listo ya somo capaces de dar el salto a la memoria de instrucciones, así que el último elemento que queda es un programa que ejecutar. He optado por un programa sencillo que simplemente implementa el algoritmo de la multiplicación. Al igual que el bootloader al no tener ni compilador ni traductor no he podido usar ensamblador y por tanto lo he escrito en código máquina.

Esta vez si, el programa se encuentra en un fichero a parte, y que se llama mult4x4. El código del programa (en código máquina) es:

El programa multiplica 3 por 3, y escribe en la RAM una cadena con el resultado (pasado a ascii), concretamente escribe "El resultado es: 9". También coloca el registro bc en la pila para después recuperarlo en de.

Con todo esto ya podemos ver en funcionamiento la CPU.

No descarto hacer un traductor de ensamblador a código máquina, total ya he tenido que hacer un desensamblador para el depurador. Aún así, cuando se estudia completamente un procesador, no es muy traumático escribir código máquina directamente.

Unas capturas de pantalla del programa, el bootloader, el diseño del código fuente y un ejemplo del depurador.

El código fuente lo subiré a github en breve.

1 2 3 4 5 6