Hola de nuevo. En esta ocasión, la reunión se celebró en el Centro Ciudadano de Guajara, en La Laguna. Gracias a nuestro compañero Fernando, por gestionar la cesión del local, y al Consejo Gestor del centro, por cedernos este espacio, que se caracteriza por ser amplio y luminoso. Se encuentra además en una zona donde resulta fácil encontrar aparcamiento, pese a estar cerca de un gran zona comercial.
Hubo una gran asistencia de amigos, y toda clase de cacharros, desde ordenadores y consolas clásicas, las nuevas minis, trabajos de soldador y programación, incluso sistemas novedosos, de los que iremos dando información según se pueda (Top Secret, For Your Eyes Only).
Hola a todos, amigos y amigas de la onda retro. Con mucho retraso, voy a compartir varias entradas sobre las reuniones de los últimos meses, mostrando fotos y el ambiente vivido.
En mayo, la reunión se celebró en la sede de nuestros colegas del Grupo Maker Kreitek, en La Laguna. Nuestro agradecimiento, por compartir este espacio con nosotros.
Sin más, algunas imágenes de la reunión:
Saludos!!
El pasado viernes, día 6 de octubre, tuvo lugar la Asamblea General anual de nuestra asociación.
En ella realizó la elección de una nueva junta directiva, una vez que la anterior había cumplido su mandato.
Todos los miembros presentes quisieron agradecer a la junta directiva saliente el trabajo realizado estos años.
Así mismo se aprobó el nuevo Reglamento de Régimen Interno, que viene a complementar a los Estatutos de la Asociación. Ambos documentos pueden consultarse desde el menú de la página web.
Uno de los problemas que solemos tener los que coleccionamos (¿quién ha dicho acaparar?) es que con el tiempo, si alguna vez los tuvimos, los corchos con los que se protegen y encajan las máquinas en sus cajas terminan deteriorándose o perdiéndose.
En este tutorial explico como he hecho unos, que si bien no son copias exactas de los originales, cumplen su función y muy bien.
En mi caso he empleado "corcho" de espuma de polietileno, el típico corcho de protección que viene en las cajas de electrodomésticos. No "el de bolitas", sino el que es como una espumas mas o menos dura.
Este tipo de espuma es más resistente que el "corcho de bolitas" (poliestireno expandido o EPS) y al cortar se retrae menos, con lo que no tenemos que estar añadiendo mucho margen a nuestra plantilla.
Para cortar las formas interiores me he hecho unas cortadora con hilo de nicromo, que se puede comprar en cualquier tienda de electrónica. Consta simplemente de el hilo por un lado fijo a la mesa y el otro tensado por un resorte. El resorte es necesario ya que al calentarse el hilo se expande y pierde tensión.
A cada extremo del hilo conectas los polos de una fuente de corriente continua. En mis pruebas con una 18W (9V, 2A) me fue suficiente. Probé con 24W (12V, 2A) y la espuma se retraía demasiado respecto a la línea a cortar.
Ten en cuenta que tendrás que montar y desmontar el hilo con cada pieza, para poder pasarlo por el hueco que hagas en el espacio interior de la espuma, y después para retirarlo.
Para pegar las diferentes capas en entre si he usado un pegamento especial para plásticos que no derrite o altera el material, cola de contacto Novopren. Lo he conseguido en Leroy Merlin.
El procedo comienza dibujando en contorno de la máquina sobre una hoja de papel. Conviene dejarlo bastante ajustado para que así los corchos no se caigan cuando estamos manipulando la máquina para sacarla o meterla en la caja. Además siempre perdemos un milímetro o dos en el propio corte.
Recortamos la plantilla.
Si el corcho llega a las teclas o hay alguna otra diferencia entre las diferentes capas y los lados de la máquina tendremos que ver si nos vale una única plantilla añadiéndole y quitándole extras o necesitamos diferentes plantillas.En mi caso, para un Atari 65XL y un Phillips MSX VG-8020 con una sola plantilla por modelo con un trocito de quita y pon para el hueco de las teclas me fue suficiente.
Después medimos en interior de la caja para obtener el rectángulo exterior de la plantilla y lo dibujamos sobre un cartón duro de 1mm o así, NO CORRUGADO. El cartón corrugado no deja los bordes precisos.
Cogemos la plantilla de papel con el contorno de la máquina y lo colocamos dentro del rectángulo en el cartón. Con una regla lo colocamos donde mejor nos venga, o centrado para mayor protección o más cerca de algún borde si tenemos que dejar espacio para los manuales o los cables.
Después recortamos el interior primero y después el cuadrado exterior.
Presentamos la plantilla en la máquina real y comprobamos que todas las medidas están correctas, incluso podemos meter la plantilla (con o sin el ordenados) en la caja para ver que todo está bien y cierra correctamente. Típicamente no habremos tenido en cuanta las lengüetas interiores y otras dobleces de la caja que harán que la tapa no cierre. Recortamos los sobrantes y listo.
Después forramos tanto el hueco interior como el exterior de la plantilla con cinta de aluminio o "platina" de cocina y pegamento. Con esto evitamos que el calor y el simple uso deteriore los bordes.
A continuación hacemos una plantilla más basta en cartón corrugado, dejando 5mm o así hasta el borde de la forrada y se la pegamos encima. Con esto hacemos que el conjunto sea rígido sin perder la definición del borde.
Ya sólo queda pegar con cinta doble cara la plantilla a la plancha de espuma de polietileno, recortar dejando margen exterior y ponernos a recortar el interior lo más pegado a la plantilla que podamos.
En mi caso no podía hacer mucha fuerza para ir más rápido ya que el hilo se combaba y el corte no salía recto. Despacito y con paciencia recortas todo el interior de todas las capas.
Acuérdate que puedes tener que añadir los parches a la plantilla principal para hacer las siguientes versiones.
Después encolamos las diferentes capas para pegarlas entre si. Aquí se organizado y comprueba antes el orden de las capas y cual va pegada a cual y en que sentido, ¡no vayas a hacer dos lados izquierdos!
!Sigue las instrucciones del pegamento! Seguramente te dirá que tienes que estar en un sitio ventilado y tendrás que dejar el pegamento reposar unos segundos o minutos antes de unir las dos partes.Pega las dos partes alineándolas por en interior.
Usa algo rígido para presionar toda la superficie por igual. En mi caso usé dos chapas y unos sargentos, pero una chapa con un peso encima presionando contra el suelo cumplirá su función.
Deja secarse el pegamento lo que te recomiendan sus instrucciones y pégale la siguiente capa. Mi diseño consistía en 2 capas y unos añadidos laterales.
Después de tener todo el conjunto "seco", es hora de recortar el rectángulo exterior. En mi versión definitiva este paso lo he hecho con un cuchillo grande cocina bastante afilado. Un cutter grande deberá valer. Intenta el corte en profundidad de una sola vez para que quede más homogéneo.
Usa un objeto con una ángulo recto para poner encima de la espuma y que sirva de guía vertical al cuchillo, como una caja pequeña, un bloque de algo...
Las primeras versiones hice el corte exterior antes de pegar las piezas y me di cuenta que no quedan igual las capas. Otra versión intenté cortar el exterior con el hilo caliente y no queda todo lo recto que me gustaría al hacerlo a pulso. El cuchillo es la opción más rápida con resultado aceptables que encontré.
Puede que te queden restos de pegamento en en interior debido al rebose del pegamento sobrante de haber pegado todas las capas. Yo simplemente lo arranqué a pellizcos. El pegamento debe estar lo suficiente mente "seco" para tener textura de chicle a estas alturas.
El resultado final lo encuentro más que aceptable
Iré guardando las plantillas de los corchos que haga por si en el futuro tuviera que hacer otro.
El sábado 23 de septiembre tendrá lugar nuestra reunión mensual, en el Centro Ciudadano de San Bartolomé de Geneto, en La Laguna. En esta ocasión tendrá lugar en la planta alta, que se encuentra después del bar.
La localización de centro ciudadano es:
Cam. San Bartolomé de Geneto, 206
38108 La Laguna, Santa Cruz de Tenerife
Google Maps
El horario será de 10 de la mañana a 8 de la tarde.
El lugar cuenta con amplio aparcamiento.
Como
siempre nuestras reuniones son abiertas a cualquier aficionado, así que
no dudes en venir a vernos.
Si vas a usar tus equipos tenemos lugar de sobra para todos. Recuerden traer regletas, alargadores y muchas ganas de charla.
El próximo sábado 5 de agosto tendrá lugar nuestra reunión mensual, en el la zona de La Cuesta.
La dirección exacta del lugar de reunión es:
Avenida de Los Menceyes, 232, 1º izquierda
La Cuesta - San Cristóbal de La Laguna
Empezaremos la reunión, como de costumbre, sobre las 10 de la mañana y en esta ocasión podremos quedarnos hasta la noche. Es posible que pidamos algo para comer en el mismo lugar, para no perder demasiado tiempo moviéndonos a un restaurante.
Como
siempre nuestras reuniones son abiertas a cualquier aficionado, así que
no dudes en venir a visitarnos.
Recuerda que, si vas a usar tus propios equipos es muy importante traer regletas y alargadores.
Si tienes cualquier duda o necesitas alguna aclaración puedes escribirnos a info@canariasgoretro.org, o entrar en el canal de charla de Telegram.
En 1999, se estrenó para Nintendo 64, Castlevania, que supuso el primer salto de la saga al mundo 3D.
A pesar de las críticas, me parece un juego bastante interesante, y desde noviembre del año pasado, he estado investigando el funcionamiento del videojuego usando métodos y programas de ingeniería inversa con el objetivo de generar documentación (ponerle nombre a las funciones / datos, averiguar cómo se estructuran los tipos de datos, etc) que sirviera de ayuda para aquellos que deseen crear ROM hacks y mods complejos para el juego, o bien para que fuese de utilidad en caso de que alguien quisiese decompilar código del juego.
Después de 8 meses, por fin he podido documentar lo suficiente del juego como para poder crear mis propios actores desde cero, inyectarlos en el juego y hacerlos funcionar. Para los que no lo sepan, un actor se corresponde con una entidad que se puede insertar en un mapa y que ejecuta su propio código, independientemente de otras entidades.
Muchos actores se corresponden con enemigos, y eso es justamente lo que he hecho hoy: programar un enemigo completamente nuevo, un Thwomp.
Los Thwomps son unos enemigos de la saga Super Mario con forma de piedra gigantesca. Su comportamiento es muy sencillo: solamente dan un pisotón al suelo cada X tiempo. A lo largo de la saga, este enemigo ha tenido varios diseños. En este artículo, vamos a reutilizar el de Super Mario 64, más que nada porque, gracias al proyecto de decompilación de Super Mario 64, disponemos del código fuente correspondiente al modelo 3D del enemigo. Este código es usado por la GPU de la N64 para dibujar al enemigo en pantalla.
Con todo esto dicho, ¡empecemos!
1. Antes que nada…
- Castlevania 64 fue programado en C, como la gran mayoría de los juegos para la N64. El compilador utilizado forma parte del toolchain de C IDO (probablemente la versión 7.1), el cual también fue usado por muchos otros juegos de N64. En GitHub podemos encontrar una versión recompilada de este toolchain. Usaremos su compilador de C en nuestro proyecto.
- Usaremos el linker mips64-ld.
- También usaremos nOVL para quitar toda la información innecesaria de los ficheros una vez compilados. Esto es necesario para que funcionen correctamente en el juego y por supuesto para que los ficheros finales no sean tan grandes.
- También utilizaremos ZAPD para poder convertir texturas en formato PNG a datos crudos que serán incluidos al fichero final.
- Finalmente necesitaremos el software de LZKN64 para comprimir los ficheros finales en el formato de compresión soportado por el juego (llamado Nagano), de manera que los podamos inyectar en la ROM.
- La inyección de los ficheros y otros parches se realizan mediante un par de scripts de Python, por lo que necesitaremos además Python 3.
Para que el enemigo funcione, debemos de reemplazar un enemigo existente del juego. El enemigo que reemplazaremos será el “Pillar of Bones”. Esto quiere decir que el Thwomp reutilizará la ID del actor Pillar of Bones (0x2081), la ID del fichero del modelo 3D (0x1B) y la ID del fichero de código, osea su overlay (0x82).
2. El struct del Thwomp y sus variables
Todos los actores siguen una estructura de datos específica, que es usada tanto para guardar variables del actor en sí como para ejecutar su código correctamente.
Nuestro Thwomp tiene la siguiente estructura:
typedef struct {
ModuleHeader header;
u8 field_0x20[4];
model_info* model;
atari_data_work* collider;
u32 timer;
f32 fallingSpeed;
u32 movingTime;
u32 waitTime;
u8 field_0x3C[0x70 - 0x3C];
actor_settings* settings;
} thwomp;
- header: Todos los actores contienen una estructura más pequeña al principio que indica cosas como su número de identificación (ID), la ID de la función que está ejecutando en este momento, un puntero a su función de destrucción, etc. Básicamente, esta estructura contiene datos relacionados con la ejecución del código del actor.
- model: Esta estructura controla el modelo 3D del actor y guarda varios parámetros asociados a este (posición, ángulo, tamaño, color, etc).
- collider: Se trata de un colisionador, una forma geométrica invisible usada para la detección de colisión entre otros colisionadores (por ejemplo, entre el actor y el jugador).
- timer: Un cronómetro usado para cronometrar los distintos estados en el que se encuentra el Thwomp.
- fallingSpeed: La velocidad de caída del Thwomp.
- movingTime: Cuando el Thwomp está subiendo, esta variable contiene el tiempo máximo que deberá estar mientras suba. Cuanto más grande sea este valor, más arriba subirá el Thwomp.
- waitTime: Cuando el Thwomp está en reposo, esta variable contiene el tiempo máximo que deberá estar mientras esté quieto. Cuanto más grande sea este valor, más tiempo estará el Thwomp en reposo.
- settings: Esta estructura de datos contiene datos de inicialización asociados a los actores dentro del mapa, tales como su posición inicial, su ID, y sus “variables” (el significado de estas variables depende del actor, véase más abajo).
- field 0x20 y field 0x3C: Para que nuestro actor funcione de manera correcta, es recomendable que las variables model y settings estén en la posición 0x24 y 0x70 respecto al comienzo de la estructura. Las dos variables “field” sirven como padding (es decir, datos de relleno sin uso), que hacen que model y settings puedan acabar en las posiciones 0x24 y 0x70.
Ahora veamos las funciones asociadas con el Thwomp. Como veremos a continuación, estas funciones son ejecutadas a partir de un array de punteros a funciones:
void (*thwomp_functions[])(thwomp* self) = {
thwomp_init,
thwomp_rise_up,
thwomp_stay_on_top,
thwomp_fall,
thwomp_hit_ground,
};
- thwomp_init: Función de inicialización. Crea y asigna un valor inicial a todos los campos del struct del Thwomp.
- thwomp_rise_up: Estado 0 -> El Thwomp está elevándose hacia arriba.
- thwomp_stay_on_top: Estado 1 -> El Thwomp está reposando en el aire.
- thwomp_fall: Estado 2 -> El Thwomp está cayendo hacia el suelo.
- thwomp_hit_ground: Estado 3 -> El Thwomp está reposando en el suelo.
Además de estas funciones está la función thwomp_entrypoint. Esta función se ejecuta siempre mientras el Thwomp esté en memoria, y normalmente se encarga de acceder a otras funciones (en el caso del Thwomp, las funciones que vimos arriba).
Finalmente veamos algunas de las variables constantes que usaremos en nuestro código:
#define THWOMP_ASSETS_FILE 0x1B
#define THWOMP_MODEL_SIZE 0.1f
#define THWOMP_COLLIDER_SIZE 400.0f
#define THWOMP_COLLIDER_DAMAGE 16
#define THWOMP_RISE_UP_SPEED 1.0f
#define THWOMP_FALLING_ACCELERATION -0.4f
#define thwomp_seg5_dl_0500B750 0x06001D98
#define THWOMP_ACTOR_ID 0x2081
THWOMP_ASSETS_FILE: La ID del fichero que contendrá el modelo 3D del Thwomp. Reemplaza al modelo 3D del Pillar of Bones.
THWOMP_MODEL_SIZE: El tamaño del modelo 3D.
THWOMP_COLLIDER_SIZE: El tamaño del colisionador.
THWOMP_COLLIDER_DAMAGE: El daño infligido por el Thwomp en el jugador.
THWOMP_RISE_UP_SPEED: La velocidad de elevación del Thwomp.
THWOMP_FALLING_ACCELERATION: La aceleración del Thwomp cuando cae hacia el suelo.
THWOMP_ACTOR_ID: La ID del actor Thwomp. Como reeemplazaremos al Pillar of Bones, usaremos su ID (0x2081)
thwomp_seg5_dl_0500B750: La “dirección” dentro del fichero del modelo 3D donde empiezan los datos asociados al modelo 3D (en otras palabras, la dirección donde empieza la display list principal del modelo). Pongo “dirección” entre comillas porque realmente no es una dirección real de memoria, sino la dirección dentro del fichero OR’d con 0x06000000 (el OR es necesario para que el juego sepa que la dirección se corresponde con una dirección de fichero, y no una dirección de memoria RAM).
3. El código del Thwomp
Ahora veamos el código del Thwomp. El código será compilado en un fichero de código llamado overlay, el cual se carga bajo demanda (es decir, solamente cuando instanciamos al Thwomp en un mapa).
La función entrypoint se
ejecuta constantemente, y se encarga de acceder a las otras funciones del actor (las que vimos en el array de funciones). Como esta función se ejecuta siempre, también incrementaremos el timer dentro de esta función.
void thwomp_entrypoint(thwomp* self) {
self->timer++;
self->header.functionInfo_ID++;
thwomp_functions[self->header.current_function[self->header.functionInfo_ID].function](self);
self->header.functionInfo_ID--;
}
La función de inicialización inicializa las variables del struct Thwomp.
- Creamos la estructura que maneja el modelo 3D del Thwomp. Le asignamos la ID del fichero que contiene los datos del modelo y la dirección dentro de dicho fichero donde empiezan esos datos.
// Create the struct that will handle the Thwomp's 3D model
self->model = ptr_modelInfo_createRootNode(4, ptr_array_8018CDE0[0]);
self->model->assets_file_ID = THWOMP_ASSETS_FILE;
self->model->display_list_address = thwomp_seg5_dl_0500B750;
- La posición del actor será la que le asignaremos en la lista de actores del mapa (véase más abajo)
self->model->position.x = self->settings->position.x;
self->model->position.y = self->settings->position.y;
self->model->position.z = self->settings->position.z;
- Las variables waitTime y movingTime las copiaremos de la lista de actores del mapa. De esta manera, podremos cambiar el tiempo de reposo y la altura máxima a la que el Thwomp subirá sin cambiar el código.
self->movingTime = self->settings->variable_1;
self->waitTime = self->settings->variable_2;
- Asignamos el tamaño del Thwomp. SIZE_MULTIPLIER es la última variable que proviene de la lista de actores. Cuanto mayor sea, más grande será el Thwomp.
#define SIZE_MULTIPLIER self->settings->variable_3
self->model->size.x = self->model->size.y = self->model->size.z = THWOMP_MODEL_SIZE * (f32) SIZE_MULTIPLIER;
- Creamos e inicializamos los colisionadores. Para crear el colisionador, antes debemos de crear una estructura cuyo fin es manejar los colisionadores (atari_base). Una vez hecho esto, creamos el colisionador, lo asignamos al atari_base, y asignamos el tamaño y el daño que inflige.
atari_base = ptr_atariBaseWork_create(self->model);
atari_base->field_0x0A |= 0x202;
self->collider = ptr_atariDataWork_create(self->model, 1);
ptr_atariBaseWork_attachCollider(atari_base, self->collider, 1);
self->collider->damage = THWOMP_COLLIDER_DAMAGE;
self->collider->size.x = self->collider->size.y = THWOMP_COLLIDER_SIZE;
- Con esta línea abandonamos la función de inicialización y vamos al primer estado (thwomp_rise_up)
ptr_goToNextFunc(self->header.current_function, &self->header.functionInfo_ID);
El Thwomp tiene 4 estados asociados, cada una de ellas es manejada por una función. Para transicionar entre cada función, llamamos a la función goToFunc, cuyo último argumento se corresponde con la ID de la función a la que transicionar (dentro del array de funciones).
Además, cada vez que se cambia de estado, el valor de timer se debe de poner a 0 para que se pueda usar en el nuevo estado.
- thwomp_rise_up: El Thwomp se eleva hacia arriba hasta que el valor del timer sea mayor que el que le asignamos a movingTime.
void thwomp_rise_up(thwomp* self) {
void (*ptr_goToFunc)(u16[], s16*, s32) = goToFunc;
// If we reached the top, stop rising up
if (self->timer > self->movingTime) {
self->timer = 0;
ptr_goToFunc(self->header.current_function, &self->header.functionInfo_ID, THWOMP_STAY_ON_TOP);
}
// Else, keep rising up
else {
self->model->position.y += THWOMP_RISE_UP_SPEED;
}
}
- thwomp_stay_on_top: El Thwomp se encuentra en reposo en el aire, hasta que el valor del timer sea mayor que el que le asignamos a movingTime.
void thwomp_stay_on_top(thwomp* self) {
void (*ptr_goToFunc)(u16[], s16*, s32) = goToFunc;
// Wait on top until reaching the time specified in waitTime
// Then start falling down
if (self->timer > self->waitTime) {
self->timer = 0;
ptr_goToFunc(self->header.current_function, &self->header.functionInfo_ID, THWOMP_FALL);
}
}
- thwomp_fall:
El Thwomp está cayéndose hacia el suelo, cuanto más tiempo está en este estado, más rápido cae. Cuando llega al suelo (es decir, cuando regresa a su posición Y inicial), el Thwomp se para y reproduce un sonido de caída (cuya ID es 0x105) en la posición del actor.
self->fallingSpeed += THWOMP_FALLING_ACCELERATION;
self->model->position.y += self->fallingSpeed;
// When reaching the ground, stop, play a crashing sound, and make the Thwomp's collider damageable
if (self->model->position.y < self->settings->position.y) {
self->model->position.y = self->settings->position.y;
self->fallingSpeed = 0.0f;
self->timer = 0;
ptr_play_sound_in_position(0x105, &self->model->position);
ptr_goToFunc(self->header.current_function, &self->header.functionInfo_ID, THWOMP_HIT_GROUND);
}
}
- thwomp_hit_ground: El Thwomp se encuentra en reposo en el suelo, hasta que el valor del timer sea mayor que el que le asignamos a movingTime. Entonces, volvemos al primer estado (subir hacia arriba), repitiendo el ciclo.
void thwomp_hit_ground(thwomp* self) {
void (*ptr_goToFunc)(u16[], s16*, s32) = goToFunc;
// Wait on the ground until reaching the time specified in waitTime
// Then start rising up, starting the cycle again
if (self->timer >= self->waitTime) {
self->timer = 0;
ptr_goToFunc(self->header.current_function, &self->header.functionInfo_ID, THWOMP_RISE_UP);
}
}
4. Lista de actores en el mapa
En el repo también he incluido una versión modificada de un mapa de pruebas que se dejaron los desarrolladores dentro del juego. Esta versión modificada contiene una lista de actores que definen tres Thwomps.
Se encuentran colocados en hilera, y cada uno es ligeramente más grande que el otro, y se eleva más alto. Esto es gracias
a las 3 variables que comentamos anteriormente.
La lista termina con una entrada cuyo propósito es de notificar al juego el final de la lista.
// Actor list
const actor_settings actor_list[] = {
{{-100, 0, -70}, THWOMP_ACTOR_ID, 40, 40, 1, 01, 00},
{{-100, 0, -170}, THWOMP_ACTOR_ID, 80, 20, 2, 01, 00},
{{-100, 0, -310}, THWOMP_ACTOR_ID, 120, 10, 3, 01, 00},
{{ 0, 0, 0}, END_ACTOR_LIST, 0, 0, 0, 0x80, 00} // "End of actor list" marker
};
Cada entrada de esta lista sigue esta estructura:
typedef struct {
vec3s position;
s16 actor_ID;
u16 variable_1;
u16 variable_2;
u16 variable_3;
u8 difficulty__spawn_setting_1;
u8 spawn_setting_2;
} actor_settings;
5. Conclusión
Con
todo esto dicho, ya solo nos falta compilar cada fichero con el compilador C de IDO, enlazarlo con mips64-ld, y quitar la información innecesaria con nOVL. Además, en cuanto al modelo 3D y el mapa, hay un paso adicional de conversión de texturas de PNG a datos crudos, lo cual se hace con ZAPD.
Una vez tengamos los binarios finales del overlay, el modelo 3D y el mapa, lo inyectamos a la ROM, reemplazando los ficheros asociados al Pillar of Bones, como dijimos anteriormente.
Todo esto ya está especificado dentro del Makefile, con lo que, una vez tengamos los programas instalados y la ROM esté en la raíz del proyecto, basta con escribir make para ejecutar todos estos pasos, y si todo sale bien, la ROM final será parcheada con nuestro nuevo enemigo.
Para acceder al mapa de pruebas, es necesario utilizar el siguiente código de GameShark:
81389EE0 001D
81389EE2 0000
81389EFE 0000
Si todo salió bien, deberíamos de poder ver a los 3 Thwomps:
Antes de finalizar… ¿por qué usamos tantos punteros a funciones en el código del Thwomp?
Pues bien, dado que los overlays de los actores son cargados bajo demanda, estos son cargados en memoria dinámica. Esto significa que la dirección de memoria de la función entrypoint siempre será distinta, pero el juego siempre se espera una dirección única.
Para resolver este problema, los desarrolladores optaron por “mapear” el overlay a la dirección de memoria 0x0F000000, de esta manera que la CPU se piense que el overlay siempre va a estar en esa dirección de memoria, y por lo tanto se pueda ejecutar sin problemas.
Sin embargo, cuando queremos llamar a una función que se encuentre en el rango de memoria de 0x80YYYYYY, la diferencia de salto es tan grande que no cabe en una instrucción de salto ordinaria (jal), por lo que el linker se quejará. Para poder hacer estas llamadas, cada salto se debe realizar a través de una instrucción de ensamblador jr REGISTRO, donde REGISTRO es la dirección completa de la función a llamar. Y para generar esta instrucción, debemos de utilizar punteros a funciones.
6. Enlaces
Por motivos ajenos a nosotros nos hemos visto obligados a cambiar el lugar de la reunión de este mes. Finalmente tendremos la reunión el mismo día 10 de junio, pero se realizará en:
Centro Ciudadano de Guajara
Calle Fraternidad 2
38108 La Laguna, Santa Cruz de Tenerife
Google Maps
Editamos la entrada para comentar que, por motivos ajenos a CGR, hemos tenido que cambiar el lugar de la reunión. Mantenemos día y hora, pero tendrá lugar en el Centro Ciudadano de Guajara, en La Laguna.
La localización de centro ciudadano es:
Calle Fraternidad 2
38108 La Laguna, Santa Cruz de Tenerife
Google Maps
El horario será de 10 de la mañana a 8 de la tarde.
Como
siempre nuestras reuniones son abiertas a cualquier aficionado, así que
no dudes en venir a vernos.
Si vas a usar tus equipos tenemos lugar de sobra para todos. Recuerden traer regletas, alargadores y muchas ganas de charla.
Esta vez escribo sobre cómo yo mismo me compliqué las cosas al querer hacer un simple juego.
La historia comienza con la buena intención de crear algo para el C64 usando BASIC. El punto de partida fue un simple programa por turnos en el que tu héroe "@" se mueve por un tablero intentando escapar de un terrible y monstruoso "%" durante un número limitado de pasos. Dicho y hecho:
Pues ahí se podía haber quedado la cosa. Pero entonces apareció el peor error que se puede cometer durante un desarrollo. La temible, por sus consecuencias, pregunta "¿Y si...?"
¿Y si... lo hago más grande?
La velocidad a la que se rehace el tablero tras cada turno no se resentía mucho, así que bien.
Pero... ¿Y si... lo hago aún más grande?
¿Y si... pongo dos monstruos?
¿Y si... colores?
Ahora me tenía que enfrentar a las limitaciones del BASIC del C64 y un listado lleno de PEEKS y POKES no me apetecía nada. Me acordé entonces de una de las muchas extensiones al lenguaje que existen, quizás la más famosa:
Recordaba que el autor sólo tenía 16 años cuando lo publicó. Gracias al
Simons' Basic, el BASIC del C64 ya es otra cosa: comandos gráficos, para situar texto en pantalla, de sonido, llamadas a procedimientos, bucles repeat, comandos para modificar caracteres, etc. Una maravilla.
Primera prueba:
Gracias al Simons' Basic, facilísimo. Con una instrucción que permite rellenar un área de la pantalla con un mismo caracter, el tiempo entre refrescos del tablero se hace aún más corto.
¿Y si... añadimos una salida (E), de forma que la partida no se acaba hasta que escapas?
(Ya van cinco "y si", ejem, ejem)
Todo bien. Mi único problema es que los monstruos (ahora M y N) son implacables y siempre me comen antes de alcanzar la salida...
(son buenos chicos, pero tienen hambre)
¿Y si... reorganizo la pantalla para mostrar información de forma más presentable?
¿Y si... la sailda lleva a otro tablero a modo de mundo paralelo?
¿Y si... en vez de letras para héroe y enemigos, hago unos gráficos?
(Nota mental: Creo que me estoy viniendo muy arriba)
Simons' Basic permite redefinir el juego de caracteres con facilidad. La salida y un monstruo ya no son letras. El 49 indica que he vuelto a poner un límite de movimientos. No está mal como exploración de las posibilidades del Simons', pero... ¡Ese no era el plan!
¿Y si... Invento una historia para el juego?
¿Y si... la hago incluyendo a mis compañeros de CGR?
Me contesto a todo que sí sin darme cuenta de que ese es el punto de inflexión hacia el desastre.
Ahora el juego es "ESCAPE FROM CGR" y cuenta con un delirante argumento:
Ahora el juego muestra en qué tablero nos encontramos y quiénes nos persiguen:
Entre una tontería y otra, el programa ya es 10 veces más grande que el inicial, si bien la mecánica es casi la misma.
¿Y si... pido a los compañeros que cada uno aporte un sprite hecho por ellos?
¿Y si... emulo a E.T. (el videojuego) y hay que recoger partes de una radio para ser rescatado?
(E.T. ... ¿En serio? ¿No había otro juego en el que inspirarse que no recordase a una catástrofe?)
Cada enemigo y el héroe Geraldo tienen su propio muñeco diseñado por los amigos. Contador de radios recogidas abajo a la derecha y eliminado el número que indicaba los turnos. La cosa se complica.
¿Y si...? ¡Nada de "y si"! Es que a mi petición de sprites a los chicos de CGR que iban a formar parte del juego me han respondido más. Ahora tengo más gráficos que personajes. Y no puedo hacerles el feo de no incluirlos en el juego.
Hmmmm...
¿Y si... cuando tenga todas las partes de la radio acude al rescate uno de los personajes "extra"?
Allá vamos, programa cada vez más absurdamente complejo. Lo de WORLD me parece excesivo para un tablero hecho de puntitos y lo cambio por ZONE. Me vuelvo loco con las facilidades para el texto de Simons' Basic y hago una pantalla de inicio rayando en la horterada con scroll de texto, nombres de los protagonistas apareciendo como en una película, sprites a lo loco por la pantalla:
Además, decido desarrollar el juego exclusivamente en las reuniones mensuales del grupo (porque en casa ya me está costando motivarme). Y claro, tenemos charlas, distracciones, risas, música SID a todo volumen... Se hace complicado y la programación avanza lentamente, muy lentamente.
("
chiquita castaña tu juego")
No me doy cuenta de que, realmente, el juego ya está terminado. Funciona. Empiezas y acabas. Cuando recoges las 5 radios, aparece un rescatador que lo aniquila todo y ganas la partida. Sólo queda el tedio de pulirlo para poder compartirlo.
Pero no, ahí está esa parte del cerebro que trabaja en segundo plano para complicarnos la vida gratis:
¿Y si... añado frases características de humor de cada un de los protagonistas que aparezcan cuando estén activos?
(¡Jajajaja!)
13 "y si" después y a pesar de que estoy agotado de ver el mismo código (cada vez más largo) en todas las reuniones, un día me doy cuenta de que es un C64 y que el C64 destacó por una cosa sobre todas las demás: el sonido. Maldita sea mi estampa, ¿ cómo voy a hacer un programa para el Commodore sin sonido ? Pero es que no tengo ni idea de sonido, pero ni idea. Nada, venga, a leer manuales a ver si me entero.
Por suerte para ustedes, las infantiles y poco armoniosas melodías que conseguí me escupiese la máquina no se pueden reproducir aquí y mejor es.
En este punto, con el juego funcional pero no acabado (falta una pantalla final tanto para cuando se gana como para cuando se pierde) y mi cabeza completamente saturada por culpa exclusiva del "y si" que no se calla, decidí abandonar. Ahí se queda. He hecho este artículo un año después de la última modificación porque no quería acercarme al juego, no me fuese a aparecer otro "y si"...
Este sábado 13 de mayo tendrá lugar la reunión mensual, en el Local de la Asociación Kreitek.
La dirección de Kreitek es: Camino San Francisco de Paula, 64, 38206 La Laguna, Santa Cruz de Tenerife (enlace a Google Maps).
Kreitek se encuentra situado en un sótano de uno de los edificios del Colegio Nuryana. La entrada es esta:
El horario será de 10 de la mañana a 6 de la tarde.
Como
siempre nuestras reuniones son abiertas a cualquier aficionado, así que
no dudes en venir a vernos.
Si vas a usar tus equipos tenemos lugar de sobra para todos. Recuerden traer regletas, alargadores y muchas ganas de charla.
Que tal, otro mes y una nueva reunión en el Centro Ciudadano de San Bartolomé de Geneto, con una afluencia muy importante a lo largo de la jornada, y con la presencia de nuevos amigos y amigas, que se han acercado a compartir un rato, informarse o charlar.
Han desfilado por la pasarela retro, los siguientes modelos:
- MB Vectrex
- Xbox
- Supernintendo Mini
- Commodore 64 (Ultimate 64)
- Raspberry Pi 400
- Nintendo Gamecube
- PlayStation Mini
- ZX Spectrum +2A
- Agon Light 2
- Sega Dreamcast
- Amiga 500 mini
- Amstrad CPC 464
Almuerzo y fin de fiesta!! ????
Saludos, amigos y amigas retromaniacos, llegó la reunión de marzo. En este encuentro, con los siguientes ordenadores y consolas:
- Appel Power Mac G4
- Amiga 1200
- MSX2 Philips VG8235
- Commodore SX-64, de Franck, para sustitución de membrana del teclado.
- PlayStation Mini
- Sega Megadrive
- Raspberry Pi
- Amiga 500 Mini
- ZX Uno
- Varios PC´s y proyectos para reparación y montaje, como el BBC Micro, Commodore VIC-20 o una consola tipo Pong
Un abrazo!
Que tal amigos, de vuelta en el mes carnavalero por excelencia. Como no sólo de disfraz vive el hombre, no hemos perdido la oportunidad de celebrar una nueva reunión en el Centro Ciudadano de San Bartolomé.
Con bastante afluencia y alguna cara nueva, hemos visto desfilar, entre otras, las siguientes estrellas invitadas:
- Xbox clásica vitaminada con HDD
- Consola Legends Flashback de AT Games
- Nintendo 64
- Nintendo Gamecube
- Sega Megradrive
- ZX Uno
- Raspberry Pi
- Mist FPGA
- PC´s varios
- MSX Toshiba HX-20 y HX-10
- Evercade
- PS Vita
Para terminar, momento cultural, concerto para viola, de la mano de Kerbi:
En esta ocasión, y dado que no fue posible reunirnos en el sitio habitual, nuestro compañero Magoric nos acogió en su casa, como ha hecho ya en ocasiones anteriores. Gracias Miguel Ángel, por tu amabilidad como anfitrión.
Con una afluencia algo menor que lo habitual, hemos disfrutado de las siguientes máquinas:
- Acorn BBC Micro, de Miguel Ángel, que se encuentra en proceso de restauración. Incluso hubo explosión y todo en la fuente! ????
- Amiga 600, "raspberryzado", con Pimiga y otras distribuciones
- Sinclair QL
- Macintosh Plus
- Amiga 500 mini
- Consolas portátiles Evercade, la original y la nueva Evercade EXP
Hasta la próxima!!
Otro nuevo encuentro, en nuestro sitio de referencia en la actualidad, Centro Ciudadano de San Bartolomé de Geneto.
En esta reunión hemos hecho nuestro propio programa de TV, gracias a Angelo, que ha montado un Amiga 1000 con el sistema Videotoaster. El maestro de ceremonias y conductor del programa ha sido nuestro colega Maxi Godkiller, aka "Monseñor Geraldo Rivera":
Algunos de los equipos que hemos visto han sido:
- Sega Megadrive Mini 2
- ZX Uno
- MSX Spectravideo 728
- MSXVR
- Amiga 500
- Consola Temco
- Sega Saturn
- Amiga 1200 (Mister inside!!)
- Macintosh Plus
- El engendro de Domingo Almighty God, un C64 y un Amiga, en un torre de PC, con pantalla incluida, una máquina muy molona!!!!:
Hemos contado con la presencia de alguien muy especial, se trata de Franck Sauer, que reside por temporadas en la isla, y es uno de los creadores, entre otros, de Agony, juego mítico para Amiga, editado por Psygnosis, y otros más como Unreal en Amiga, Outcast y Alexander en PC. También ha participado en sagas como Heroes of Might and Magic. Nos ha enseñado un mini arcade que ha fabricado, basado en Nano 8 con pantalla CRT, que nos ha encantado!! Thank you Franck for meeting with us, and spend some time in this madhouse!!
Saludos!!!
Saludos a todos, han pasado unas cuantas lunas (digo, reuniones...), desde la anterior entrada sobre nuestros encuentros. Para ponernos al día, lanzaré una serie de actualizaciones, con las mejores imágenes de máquinas y personal que asistió, así que sin mas, vamos allá:
Además del cacharreo y las risas, hemos contado con las siguientes máquinas:
- Commodore PC-10-III, que no es lo que parece. Se trata de una máquina que no está en funcionamiento, pero que usa el hardware mediante una Raspberry Pi.
- MSXVR
- Raspberry Pi 400. Un todo en uno ideal, integrado en un teclado.
- Retron Sq. Maquina de Hyperkin, que permite usar cartuchos originales de Game Boy (clásica, color y Advance), vía HDMI.
- C64 Mini
- PS2 con HDD y FreeMcBoot.
- MIST, potencia desde FPGA.
Un abrazo!!!
BLACK SOUND. Un recuerdo a 4 manos. Por Walls & Magoric.
A principios de los 90, el Commodore Amiga tenía una fuerte presencia entre la comunidad de usuarios de ordenadores domésticos en el Tenerife en el que yo vivía, pero por extensión en todo el territorio nacional. Su colección de software era inacabable y entre todo este, hubo un hueco para el llamado ‘freeware’ o ‘shareware’, programas gratuitos en su totalidad o en parte, que algunos desarrolladores donaban a la comunidad para su uso. Aprovechando las posibilidades de uno de estos programas, “Perfect Sound”, un gran amigo, Juan Carlos Walls y un servidor, con la asesoría para la parte analógica del proyecto de uno de los profesores de la facultad donde cursábamos estudios de ingeniería informática, Alejandro Ayala, diseñamos, construimos y comercializamos un digitalizador de audio. Esta es la historia de como lo hicimos.
Francamente, el recuerdo que tengo de los detalles es vago y desperdigado en el queso de gruyere que siempre ha sido mi memoria, pero trataré de hilar la historia de la forma mas coherente posible.
En aquellos días había un grupo de motivadísimos usuarios de Amiga en la universidad entre los que se encontraba Walls, que así era como le conocimos, llamábamos y seguimos llamando. No recuerdo a quien se le ocurrió la idea de hacer el aparato, seguramente a Walls porque él era el que tenía el ordenador y el software, pero apostaría que fue a mi a quien se le ocurrió comercializarlo. La idea era hacer algo sencillo, barato y buscar la manera de venderlo rápidamente. En aquel entonces, no veíamos la informática de una forma tan romántica como la percibimos actualmente refiriéndonos a los actualmente clásicos y como estudiantes agradecíamos cualquier tipo de ingresos.
Lo primero que hicimos fue elegir un chip como base del diseño que convirtiera la señal de audio en un patrón digital. Un conversor analógico a digital. Por desgracia, una de las decisiones “empresariales” que tomamos, y de esto si que me acuerdo bien, fue limpiar el chip de su referencia por si aquello tenia recorrido comercial fuera más difícil su reproducción, por lo que no tenemos manera de saber, por ahora, que chip específicamente usamos, pero debió de ser uno bastante barato y muy común, porque compramos todas las unidades, que fueron como unas veinte, en la tienda local de electrónica, que por cierto, aun sigue existiendo.
En cuanto tuvimos el chip, lo montamos en una placa de prototipo y lo conectamos al Amiga, vimos que necesitaríamos algo de electrónica adicional. Dentro de lo digital, mas o menos “caminábamos” , pero como hubiera que acompañar de analógica, estábamos perdidísimos, por lo que tiramos de Alejandro Ayala, profesor de electrónica analógica en aquel curso, que prácticamente nos hizo el diseño completo de la circuitería. El laboratorio de la facultad ayudó enormemente. No tardamos mucho porque, como se vé en las fotografías que acompañan el artículo, es un diseño enjuto que aprovechaba el puerto paralelo del ordenador, normalmente destinado a conectar la impresora. Técnicamente era de una sencillez casi trivial que hasta aprovechaba la corriente del puerto paralelo para alimentar el circuito.
Una vez tuvimos el prototipo quedaba la parte menos divertida. Hacer de aquello algo vendible. Lo primero fue hacer un diseño para la placa. Eso lo hizo Walls atendiendo al prototipo y tratando de hacer un diseño de una sola cara, un diseño compacto hecho a mano, donde, como se vé, incluyó la firma de ambos. En aquel tiempo “WallySoft & MAR”.
Del proceso de replicación me encargué yo. En aquel entonces lo hice de la única forma que conocía, un proceso artesanal que consistió en fotocopiar el diseño de Walls varias veces en una hoja para poder sacar de una o dos veces todas las unidades, repasar las imperfecciones, volver fotocopiar la hoja resultante en tres o cuatro fotolitos de los que se usaban para proyectar las transparencias en las presentaciones y clases de la época, y alineándolos entre si para tener mayor densidad de negro, los hice uno superponiéndolos usando cinta adhesiva en una mesa para que no se moviesen, colocando la placa sensible a la luz debajo y un foco de una lampara flexible encima durante un rato. “Quemé” las placas que después revelé y quedaron como una matriz de cada una de las unidades, que separé con una sierra de mano y con un pequeño taladro, que aún conservo, hicimos los agujeros para los componentes.
Las carcasas las compramos en la misma tienda de electrónica donde adquirimos todo lo demás, por lo que solo nos faltaba las partes menos técnicas. El diseño y el nombre se eligió entre dos propuestas, una de cada uno, pero no hubo mucha duda en eso, escribimos las instrucciones y embolsamos finalmente las unidades, ya listas para su venta, incluyendo en el pack un disco con el software, que no vendíamos sino que regalábamos con el hardware, y una hoja con las instrucciones.
Conocía y me llevaba bastante bien con la gente de Hardtracks Systems, una tienda local de venta de ordenadores que aun sigue funcionando, a las que les mostré el producto y se sintieron interesados de inmediato comprando todas las unidades. Eran auténticos fans del Amiga. Por desgracia, nuestro digitalizador era “mono” y apenas unas pocas semanas después de comprarnos los aparatos salió otro “estéreo” con un precio similar, por lo que solo vendieron al publico unas pocas unidades. Recuerdo que Walls y yo pensamos en hacer otro estéreo y que una posibilidad teórica que barajamos fue dividir la frecuencia de muestreo para abarcar cada canal, pero enseguida vimos que ni podíamos competir ni había tanto mercado a nuestro alcance como para intentarlo por lo que ese fue el fin del proyecto.
Todo esto cayó, al menos para mi, en el mas absoluto olvido hasta que hace algunos años me encontré un par de unidades en una caja. He de reconocer que me dibujó una sonrisa y provocó que diéramos una charla en Canarias Go Retro sobre la experiencia, volviendo a la oscuridad hasta que, recientemente, en un programa de AmigaWave, volví a recordarlo y me llevó a escribir estas lineas.
Miguel Ángel Ramos (Magóric).
El diseño de circuito que usábamos era muy básico y a veces el chip "se colgaba" y no digitalizaba la entrada de sonido. Como el hardware no era controlado por el software, sino que simplemente enviaba el resultado de la digitalización continuamente al puerto paralelo independientemente de si había un programa que lo estuviera leyendo o no, no teníamos manera de reiniciar el chip por software cuando este cuelgue ocurría, por lo que añadimos un pulsador que al accionarlo reiniciaba el chip y resolvía este problema. Como esto se podía ver como un fallo de diseño o un inconveniente, fuimos muy cucos dándole la vuelta a la tortilla haciendo de este defecto una virtud y en las instrucciones dimos a entender que este botón lo que hacía era ajustar el chip a las características de la fuente de sonido actual y que con esa acción se podía obtener la máxima calidad en el digitalizado.
Una de las cosas que más quebraderos de cabeza nos dio a la hora de construir el aparato, fue el conector del puerto paralelo. Los conectores del Amiga eran de tipo D-SUB con una tuerca a cada lado en la que se podía acoplar el tornillo correspondiente del conector de carcasa del aparato que se conectara al ordenador. Es un acople del mismo tipo que los cables S-VGA y proporciona una conexión muy sólida. El problema era que nuestros conectores también eran del mismo tipo que los del Amiga, es decir, que también tenían tuercas y para que se conectara en el puerto paralelo del amiga, estas tuercas tenían que quitarse. Pero al hacer esto, se soltaba también la placa metálica que rodeaba los pines y que da nombre al conector. Probamos varias maneras de evitar esto. Lo intentamos sustituyendo las tuercas por tornillos, pero seguían abultando demasiado. Probamos con conectores aéreos, pero esto suponía cambiar el diseño a un diseño de doble cara para poder soldar el conector en el borde de la placa en lugar de en agujeros y eso aumentaba los costes y complicaba muchísimo el proceso artesanal. Pensamos en serrar las tuercas hasta dejarlas casi a ras de la chapa, lo justo para que pudieran sujetar la misma pero que no interfirieran con la conexión, pero era un proceso muy laborioso. Al final decidimos quitar las tuercas y pegar las chapas con pegamento de cyanocrilato. Reconozco que no fue la mejor decisión y a día de hoy, de optar nuevamente por esta opción, habría utilizado pegamento de contacto, que es más flexible, pero en aquel momento el cyanocrilato era algo relativamente nuevo y parecía como la opción más resistente. Si alguien que lea estas líneas tiene un Blacksound y se le ha caído esta chapa, le aconsejo limpiar los restos de cyanocrilato y volverla a pegar con pegamento de contacto.
Juan Carlos Walls.