Daegon, mes CCCLXX

Por Javier Albizu, 23 Abril, 2023
1. Sobre la escritura:
Numericos y letras de estas últimas cuatro semanas:

Documento principal: 11.659 palabras y 20 páginas.
Descartes: 17,290 palabras y 47 páginas.

Esto hace 861 palabras “finales” y 2.259 de descartes. O lo que es lo mismo, 3.150 palabras en total. La cifra más baja en lo que llevamos de año (y eso que en este periodo de tiempo hemos tenido unos cuantos festivos de más).

Sin embargo, no todo es malo. Hemos llegado ya hasta la “recta final” del proceso. Solo quedan dos páginas y media para dar por concluida esta segunda revisión. Con un poco de suerte, a lo largo de este mes confío en empezar ya con su “epílogo” (porque yo soy así, e incluso los relatos cortos tienen su prólogo y su epílogo).

Sigo sin tener claro qué relato seguirá a este, si ese relato lo precederá cronológicamente, o qué haré con el orden final de esta colección. No tengo ni idea, y el tiempo que me queda hasta que tenga que tomar esa decisión va siendo cada vez menor.

Por otro lado...
Bueno, mejor dejo eso para la parte de miscelánea.

2. Sobre el dibujo
En lo que respecta al dibujo, cuando estamos en casa seguimos probando y experimentando. Tirando líneas y manchas para ver qué sale cuando usamos referencias, cuando no lo hacemos, y cuando nos quedamos a medio camino. Como casi siempre, los resultados dependen del día que tenga uno.

Sigo sin hacerle demasiado caso al color y centrándome en los grises... pero los grises también son puñeteros cuando no tienes claro cómo contrastarlos en condiciones. Quedan cosas curiosas, y a veces incluso interesantes... pero, por norma general, demasiado artificiosas (y la falta de fondos o cuerpos tampoco ayuda a generar la sensación de “imágenes de verdá”).

Como siempre, los avances y retrocesos se pueden ver por aquí1.

3. Sobre la programación
Llegamos hasta quien muy posiblemente (de haber alguno) haya sido el culpable de la bajada de rendimiento en la escritura.

Vayamos por partes... porque han sido unas cuantas.

Por un lado, el propósito con el que comencé este mes era el de ser capaz de trabajar con “proyectos complejos y bien estructurados”. Esto es, código separado en varios archivos de manera que pueda organizarlo en condiciones (y que no me vuelva loco yendo de arriba a abajo en un único archivo).

Con este propósito en mente comencé una búsqueda. Un rastreo por Github a la caza de un proyecto para C64 cuyo código estuviese separado y, a ser posible, hubiese sido creado con CC65.
Esto me llevó hasta dos repositorios2. Hasta dos lugares que no me servían al cien por cien.
Sea como fuere, era un punto a partir del que podía seguir trabajando.

En la parte que respecta a DraCopy, este programa está creado con CC65, así que guay. Por otro lado, está hecho en C, no en ensamblador... y por ahí vamos mal (por el momento).
Por su parte, Dustlayer (cuya web web ya conocía3, pero cuyo repo no había visitado antes), está hecho en ensamblador, guay por ese lado... pero ha sido creado con el “motor Dust”... que está desarrollado utilizando Acme.

Bueno. Como decía, era un comienzo.

Para empezar con los ánimos en condiciones, ambos compilaban y funcionaban a la primera. Buena señal.

Para seguir, era capaz de “portar” el código de Acme a Kick Assembler, y lograr que funcionase... más o menos.
El más o menos viene como consecuencia de la manera en la que se importan los “recursos” (fuentes, sprites y sonidos) en el ejemplo de Dustlayer.
Estos objetos están disponibles en formatos exportados por tres programas para los que ACME trae rutinas de importación. Herramientas que no sé si están disponibles en Kick o CC65. Sí que conozco y tengo instalados dos de estos programas4 pero, por el momento, no les he prestado demasiada atención (aunque todo llegará). A su vez, ambos son programas para Windows, pero funcionan sin problemas bajo WINE.
Sea como fuere, consideré que este no era el momento para ponerme con ellos. Así, y dado que lo que yo quería era “ir rápido”, en lugar de mirar cómo importar estos archivos en estos compiladores, a base de abrir sus binarios con un editor hexcadecimal, y mirar en el monitor de VICE las posiciones de memoria en las que ACME deja los datos, puse el código a palo seco a base de instrucciones .byte directamente a sus posiciones de memoria.
Tras unas pocas pruebas, conseguía que Kick compilase correctamente este tema, el programa arrancaba, había música (aunque no exactamente la misma, algo que tengo que mirar con mayor detenimiento), ciclos de color, la nave surcaba la pantalla y era capaz de moverla de arriba a abajo con el teclado... pero el texto no aparecía en pantalla.
La rutina de pintado del texto no parecía ser compatible con pasar las cadenas tal y como las he puesto en los “Hola Mundo” que había encontrado por ahí.
Mirando el binario final, veía que esa manera de introducirlo dejaba el códigos de los caracteres en ASCII, pero lo que generaba ACME eran códigos hexadecimales que no se correspondían con ellos.
Al final, para que apareciese el texto tuve que hacer algo parecido a lo del resto de recursos, copiar los bytes de la memoria y meterlos a palo seco tal cual.

Con esto, ya tenía dos de tres. Quedaba por hacer funcionar la parte de CC65 (con la que me había estado pegando en paralelo a la de Kick) que, precisamente, era en la que más interesado estaba.

Pero no había manera. No había errores en el compilado o el ensamblado, pero aquello no arrancaba. El report de CC65 me decía que todo estaba donde debía, pero el monitor de VICE me decía que en las posiciones de memoria donde tenían que estar los recursos solo había basura.
Stupendo todo.

Por otro lado, comparando los binarios generados por ACME y Kick con el editor hexadecimal, veía que “casi” eran iguales. Comparándolos con lo que generaba CC65, aquello no tenía nada que ver (ni en tamaño ni en ubicación de los datos).

Tras mirar por la red de redes, llegué hasta un par de entradas5 que parecían señalar dónde podía estar la causa. El problema parecía estar en el linker, y en cómo va colocando los datos CC65 en el binario final. Tocaba leer la documentación oficial6 y entrar en el mundo de los archivos de configuración. Descubrir que, cuando ejecutaba “cl65” para compilar, este llamaba por debajo a ca65 y a ld65. Empezar a poner parámetros como un loco para tratar de obtener el mayor cantidad de información posible del proceso.

Con esto llegamos hasta el fantabuloso proceso de compilado / ensamblado / linkado de CC65.

Veamos una reconstrucción chorra de manera desglosada:

cl65 -d -O -Or -Os -r -g -v -vm -I -W2 -Wl
-t c64
-m report/Dustlayer_CC65_Ori/Dustlayer.map
-l report/Dustlayer_CC65_Ori/Dustlayer.report
-Ln report/Dustlayer_CC65_Ori/Dustlayer.labels
--dbgfile,report/Dustlayer_CC65_Ori/Dustlayer.dbg
-o prg/Dustlayer_CC65_Ori/Dustlayer.prg

src/Dustlayer_CC65_Ori/Dustlayer.s

Esta instrucción (que realmente es una única línea, pero que he desglosado para mayor claridad), desencadena lo siguiente:

- Primero, ejecuta ca65 para generar el código objeto:

Executing: ca65 -g -v -l report/Dustlayer_CC65_Ori/Dustlayer.report -t c64 src/Dustlayer_CC65_Ori/Dustlayer.s

- Después, ejecuta ld65 a partir de los archivos objetos que ha generado ca65 y los enlaza con “c64.lib”:

Executing: ld65 -v -vm
-t c64
--dbgfile report/Dustlayer_CC65_Ori/Dustlayer.dbg
-m report/Dustlayer_CC65_Ori/Dustlayer.map
-Ln report/Dustlayer_CC65_Ori/Dustlayer.labels
-o prg/Dustlayer_CC65_Ori/Dustlayer.prg
src/Dustlayer_CC65_Ori/Dustlayer.o
c64.lib

- Para terminar colocando las cosas de acuerdo a los parámetros definidos en el archivo de configuración:

Opened 'prg/Dustlayer_CC65_Ori/Dustlayer.prg'...
Dumping 'LOADADDR'
Writing 'LOADADDR'
Dumping 'HEADER'
Writing 'EXEHDR'
Dumping 'MAIN'
Writing 'STARTUP'
Writing 'CODE'
Writing 'RODATA'
Writing 'DATA'
Writing 'INIT'
Writing 'ONCE'

Algunas de las cosas que veía por ahí me sonaban. Cosas como los segmentos "STARTUP", "INIT", "ONCE" y "CODE" que me obligaba a poner CC65 si quería que la cosa compile (pero que no tenía ni idea de a qué atendían).

Mirando en la docu de ld65 veía que se hacía referencia a las áreas de memoria de la máquina de destino, así que me puse a jugar con aquel concepto basándome en los ejemplos que venían en la documentación y el archivo de configuración que venía con “DraCopy”.

Sí que vi que, tras crear tanto en el archivo de configuración como en el de definición de los recursos unos segmentos a los que llamé “SONIDO”, “GRAFICOS” y “FUENTES”, y añadir al comando de compilación un “-C Dustlayer.cfg”, se producían cambios.

Con esto, aparte de sumar estas líneas:

Writing 'SONIDO'
Writing 'GRAFICOS'
Writing 'FUENTES'

también cambiaba las cosas de sitio... aunque seguía sin dar con la tecla. Y no daba con ella porque había tenido una mala idea. Porque aquellos ejemplos de la documentación estaban bastante incompletos, y el de DraCopy lo había hecho alguien que sabía cosas que yo ignoraba totalmente.

No fue hasta que, tras mirar en el archivo “.map” que genera la compilación, en una de las ejecuciones me fijé en esto:

[linker generated] /usr/share/cc65/cfg/c64.cfg(6)

Tras echar un vistazo a ese archivo, algunas piezas terminaron de encajar en mi cabeza. CC65 (al igual que Acme y Kick, supongo) siempre usa un archivo de configuración que le indica las posiciones de memoria de la máquina de destino. Si le pones el parámetro “-t c64” utiliza el que trae de serie. Si le pones el “-C archivo” ignora el “-t” y se queda con el que le pasas (por eso en la docu dice que no se pueden usar los dos parámetros a la vez).

De todas formas, añadiendo mis segmentos “personalizados” a una copia de ese archivo de configuración tampoco logré que aquello funcionase.
No fue hasta que, tras probar diferentes configuraciones, compilar, recompilar y comparar los binarios finales nmil veces, di con la tecla.

Antes de aquello me había fijado en que, en algunas de las ocasiones en las que había compilado con mi archivo de configuración “mal formado”, el binario empezaba por los bytes hexadecimales “01 10”. Por otro lado, cuando lo hacía con uno basado en el archivo que traía CC65, el binario empezaba por “01 08”.

Por un lado, sabía que 1001 era la posición inicial en la que el programa tenía que empezar a escribir los datos y, al verlo, se me había pasado por la cabeza que podía ir por el buen camino. Por otro, 0801, si bien no significaba nada para mi programa, sí que era una posición de memoria que me sonaba. A su vez, entre otros lugares, aquel era el valor que aparecía en la sección “FEATURES { STARTADDRESS: default ” del archivo de configuración.

Sumando dos y dos, confirmé lo que ya sospechaba de antes. El compilador estaba escribiendo ese valor “a la inversa” (esto es, en litte-endian7) al principio del binario.

Fue poner 1001 en el “STARTADDRESS” y que todo funcionase... un tiempo después.

Porque la cosa no podía ser tan fácil.

Había estado cacharreando con diferentes maneras de separar los archivos, y tenía dos copias del código. Una en el formato original (esto es, un único archivo que llama de manera secuencial a todos los demás en base a includes), y otro “a la manera de C”, definiendo variables globales, haciendo exports, generando un código objeto por cada archivo, y sumándolos todos al final.
En el escenario “original” el código compilaba y funcionaba todo. En el escenario “personalizado” compilaba, y las cosas parecían estar en su sitio, pero aquello no arrancaba.

Y no lo hacía porque había discrepancias en la parte del “código” (que no en la de los recursos). A la hora de enlazar los objetos (siempre el puñetero linker), aquellos que no tenían un segmento dedicado, me los colocaba en memoria por orden alfabético del archivo que lo contenía. Con esto, primero me metía el archivo de configuración del equipo (config_sprites.s), luego iba el de las cadenas de texto (data_text.s) y a este le seguía el que tenía la rutina de inicio del programa (Dustlayer.s).
El equipo se configuraba, se encontraba con algo que no interpretaba como código, y se quedaba en el sitio.

Una vez entendido esto, y creado un segmento más al que llameé “TEXTO” y al que le asigné su memoria bien lejos del “main”, la cosa ya funcionó correctamente.

Así que me ha costado cosa de un mes conseguir compilar un puñetero programa de mielda, pero no me quejo. Por el camino he aprendido un poco más del Commodore, de los compiladores (y de los makefiles, ya que estamos).
Ahora, haciendo un make me escupe todo esto:

make

mkdir -p obj/Dustlayer_CC65
mkdir -p prg/Dustlayer_CC65
mkdir -p obj/Dustlayer_CC65
mkdir -p report/Dustlayer_CC65

rm -rf src/Dustlayer_CC65/*.o obj/Dustlayer_CC65/* prg/Dustlayer_CC65/Dustlayer.prg

cl65 -d -O -Or -Os -r -g -v -vm -I -W2 -Wl
-C src/Dustlayer_CC65/Dustlayer.conf
-m report/Dustlayer_CC65/Dustlayer.map
-l report/Dustlayer_CC65/Dustlayer.report
-Ln report/Dustlayer_CC65/Dustlayer.labels
--dbgfile,report/Dustlayer_CC65/Dustlayer.dbg
-o prg/Dustlayer_CC65/Dustlayer.prg

src/Dustlayer_CC65/config_resources.s
src/Dustlayer_CC65/config_sprites.s
src/Dustlayer_CC65/constantes.s
src/Dustlayer_CC65/data_text.s
src/Dustlayer_CC65/Dustlayer.s
src/Dustlayer_CC65/sub_check_keyboard.s
src/Dustlayer_CC65/sub_clear_screen.s
src/Dustlayer_CC65/sub_color_cycle.s
src/Dustlayer_CC65/sub_update_ship.s
src/Dustlayer_CC65/sub_write_text.s

Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/config_resources.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/config_sprites.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/constantes.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/data_text.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/Dustlayer.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/sub_check_keyboard.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/sub_clear_screen.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/sub_color_cycle.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/sub_update_ship.s
Executing: ca65 -g -v -l report/Dustlayer_CC65/Dustlayer.report -t c64 src/Dustlayer_CC65/sub_write_text.s

Executing: ld65 -v -vm
-C src/Dustlayer_CC65/Dustlayer.conf
-m report/Dustlayer_CC65/Dustlayer.map
-Ln report/Dustlayer_CC65/Dustlayer.labels
--dbgfile report/Dustlayer_CC65/Dustlayer.dbg
-o prg/Dustlayer_CC65/Dustlayer.prg

src/Dustlayer_CC65/config_resources.o
src/Dustlayer_CC65/config_sprites.o
src/Dustlayer_CC65/constantes.o
src/Dustlayer_CC65/data_text.o
src/Dustlayer_CC65/Dustlayer.o
src/Dustlayer_CC65/sub_check_keyboard.o
src/Dustlayer_CC65/sub_clear_screen.o
src/Dustlayer_CC65/sub_color_cycle.o
src/Dustlayer_CC65/sub_update_ship.o
src/Dustlayer_CC65/sub_write_text.o
c64.lib

Opened 'prg/Dustlayer_CC65/Dustlayer.prg'...
Dumping 'LOADADDR'
Writing 'LOADADDR'
Dumping 'MAIN'
Writing 'SONIDO'
Writing 'GRAFICOS'
Writing 'FUENTES'
Writing 'TEXTO'
Writing 'CODE'
Writing 'RODATA'
Writing 'DATA'
Writing 'BSS'

mv src/Dustlayer_CC65/*.o obj/Dustlayer_CC65

Pero, bueno, esta era la parte fácil. Ahora es cuando toca volver a “lo duro”. Al ensamblador.
Paciencia.

4. Miscelánea
No sé qué puede haber sido8. Si la culpa la tendrá el haberme leído el “Elusive Shift” de Peterson, los vídeos del señor Pamundi, o que ya tocaba otra vez, pero me han entrado ganas una vez más de retomar el tema rolero (daegonita).

Es probable que en algún momento de un futuro no muy lejano trate de retomar el tema de filosofar sobre el rol. Sobre la manera en la que veo la creación de partidas.
A ver si trayendo la cosa por aquí sale de una manera un poco más fluida que cuando he intentado hacerlo de manera “formal” para Daegon (y si puedo aprovechar allí algo de lo que salga por aquí).

No sé si llegará una vez que termine con el Arcanus Anotado, o si se convertirá en un segmento semanal más dentro del ciclo de los actuales,
O igual no pasa nada y se me pasa el calentón en unos días. Vete tú a saber.
Pero la idea está por ahí dando vueltas.

Enlaces:

1. Dibujos 2023

2. Va de repos
- DraCopy and DraBrowse
- Dustlayer en Github

3. Dustlayer
- Dustlayer
- Dustlayer Episode 3-1: Spritro - an Intro with a Sprite

4. Programicas
- SpritePAD
- CharPAD

5. Centrando el tiro
- Putting code into two different memory areas with cc65/ca65
- CA65 Segment Memory Map issue

6. La docmentasió
- CC65
- CL65
- CA65
- LD65

7. Endianness

8. A saber
- The Elusive Shift: How Role-Playing Games Forged Their Identity
- La serpiente de dos cabezas: El reseñón - Episodio 1
- La serpiente de dos cabezas: El reseñón - Episodio 2
- La serpiente de dos cabezas: El reseñón - Episodio 3
- La serpiente de dos cabezas: El reseñón - Episodio 4

El contenido de este campo se mantiene privado y no se mostrará públicamente.

Plain text

  • No se permiten etiquetas HTML.
  • Las direcciones de correos electrónicos y páginas web se convierten en enlaces automáticamente.
  • Saltos automáticos de líneas y de párrafos.