Lección 3
En esta tercera lección vamos a introducir un poco de teoría. Como ya no vamos a modificar más el manic.asm, partimos del manic.bin de 32768 bytes y comentamos la línea del make.bat que ensambla dicho archivo. Además, en esta lección tampoco vamos a tocar el loader.bin, así que ni siquiera necesitamos el ensamblador:
Código: Seleccionar todo
rem SjAsmPlus loader.asm
rem SjAsmPlus manic.asm
GenTape manic.tzx ^
basic 'ManicMiner' 10 loader.bin ^
data manic.scr ^
data manic.bin
Habréis observado un manic.tzx en lugar del anterior manic.tap. Esto es así porque en esta lección vamos a manejarnos a bajo nivel y el formato TAP no nos vale. Los bloques que ya hemos visto: "basic", "hdata" y "data" son los de más alto nivel, es decir, generan cargas estándar con tiempos estándar. Con estos 3 bloques podemos generar tanto TAPs, como TZXs, como WAVs tan sólo cambiando el nombre del archivo.
Lo que vamos a hacer en un primer momento es generar un archivo TZX equivalente pero con otros componetes de más bajo nivel (que no permiten crear TAPs), para luego hacer ciertos ajustes que acorten el tiempo de carga. Pero antes de introducir estos nuevos bloques, un poco de teoría.
Lo primero de todo es distinguir bloques lógicos de bloques físicos. Un bloque "basic" es un bloque lógico compuesto por 2 bloques físicos (cabecera y datos). Lo mismo pasa con el bloque "hdata". Sin embargo el bloque lógico "data" contiene un único bloque físico de datos. Esto se supone que lo sabíamos de anteriores lecciones pero nunca viene mal darle un repaso.
Un bloque físico contiene 4 partes diferenciadas:
- Tono guía. En inglés se suele usar la palabra "pilot" en lugar de "leader tone". Es un tono grave de 808Hz que avisa cuando va a comenzar un nuevo bloque. En carga estándar viene indicado por las bandas rojo y cian. Tiene distinta duración dependiendo de si el bloque físico es de cabecera o de datos: si es de cabecera dura unos 5 segundos; si es de datos, 2 segundos. Durante el primer segundo la ROM suele esperar sin hacer nada, aunque esto también lo hace si hay cualquier ruído en la cinta. Luego se asegura de que haya 512 pulsos guía después (unos 320ms) para empezar a detectar el sincronismo. La duración de un pulso (media onda) del tono guía es de 2168 ciclos de reloj. El reloj base que se toma es de 3.5Mhz (modelos 48K), siendo 285.7 nanosegundos la duración de un ciclo.
- Sincronismo. En inglés "sync". Son dos pulsos cortos de duración 667 y 735 ciclos respectivamente. Sirven para indicar que ya se ha acabado el tono guía y que lo que viene después son los datos. El primero es un pelín más corto por retardos de instrucciones (en la rutina SAVE). La razón de que sean dos es para equilibrar ondas asimétricas. Si el azimut está muy alto (o muy bajo) no resultaría raro tener un tono guía de 1500 ciclos alternado con otro de 2836 ciclos. No habría ningún problema porque la suma es 2*2168 ciclos y se detectan de 2 en 2 (o en onda completa).
- Datos. Aquí se codifican bit a bit todos los datos del bloque, incluido el byte flag de comienzo y el byte checksum del final. Se envian primero los bits de mayor peso y cuando llegamos al bit 0 pasamos al siguiente byte. En carga estándar se señaliza con bandas azules y amarillas en el borde. El tipo de modulación que se emplea es muy sencilla, se llama FSK, y se trata de codificar el bit 0 con dos pulsos seguidos de 855 ciclos cada uno, y el bit 1 con dos pulsos del doble de duración (1710 ciclos).
- Pausa. Es un momento de silencio que se incluye al final de cada bloque (o al comienzo según se mire) para dar tiempo al Z80 a que haga lo que tenga que hacer, por ejemplo descomprimir una pantalla o mostrar una pequeña animación. También para que sea más fácil reconocerlos por el oído, si estamos buscando un bloque en concreto. Su duración difiere si el bloque físico es de datos o de cabecera. Tras un bloque de cabecera se inserta una pausa de 1 segundo; si es de datos son 2. La razón: tras una cabecera siempre se ejecuta el mismo código de ROM de duración fija (y pequeña). Con un segundo hay de sobra para el reconocimiento acústico.
Vista ya la teoría entremos en materia. Con GenTape podemos generar bloques físicos con los parámetros de tiempo que queramos, o incluso a más bajo nivel cualquier parte de un bloque físico (las que acabo de explicar) por separado.
Para generar bloques enteros tenemos el comando "turbo", mientras que para generar partes por separado tenemos los comandos "pilot", "pulse", "pure" y "pause" respectivamente. Como tenemos 2 bloque físicos (pantalla de carga y juego) voy a codificar el primer bloque con "turbo", mientras que en el segundo bloque emplearemos todo lo demás.
Una cosa muy importante. Tanto en "turbo" como en "pure" estamos trabajando a bajo nivel, y a diferencia de los tipos anteriores ("basic", "hdata", "data") no se insertan ni el byte flag al comienzo ni el checksum al final. Resumiendo, hay que coger un editor hexadecimal y añadir manualmente esos 2 bytes a los binarios. Para saber cuál es el checksum necesario puedes probar primero con un bloque "data" y ver cuál es el valor correcto (con Tapir o con un editor hexadecimal). Esto lo indico en el binario (que he añadido flag y checksum) añadiendo _flag_chk al final del nombre. Así, a "manic_flag_chk.scr" le he insertado los bytes $FF y $FF al principio y al final, mientras que en "manic_flag_chk.bin" han sido $FF y $19.
Siguiendo el orden que se muestra en la ayuda...
Código: Seleccionar todo
| turbo <pilot_ts> <syn1_ts> <syn2_ts> <zero_ts> <one_ts>
<pilot_ms> <pause_ms> <input_file>
...voy introduciendo los datos estándar, que son los mismos que he explicado antes. Al tener el tipo "turbo" una gran cantidad de parámetros yo suelo partir la línea en dos para no hacerme un lío, pero esto ya depende de lo que cada uno considere:
Código: Seleccionar todo
turbo 2168 667 735 ^
855 1710 1980 2000 manic_flag_chk.scr ^
Con esto ya tenemos un bloque físico idéntico al anterior, pero escrito a bajo nivel. Recuerdo que el bloque anterior se codificaba así:
Esta es la pantalla de carga. Nos queda el último bloque, el que contiene el juego. Como he avanzado antes aquí vamos a emplear todos los subloques existentes, excepto el "pause", ya que el bloque "pure" te permite añadir una pausa al final.
Veamos primero la ayuda de los tipos que vamos a usar:
Código: Seleccionar todo
| pilot <pilot_ts> <pilot_ms>
| pulse <M> <pulse1_ts> <pulse2_ts> .. <pulseM_ts>
| pure <zero_ts> <one_ts> <pause_ms> <input_file>
Recuerdo que el bloque a generar es equivalente a este:
Pues bien, siguiendo los valores de ciclos y duración que he explicado en la teoría, para generar el último bloque lo hacemos de la siguiente manera:
Código: Seleccionar todo
pilot 2168 1980 ^
pulse 2 667 735 ^
pure 855 1710 2000 manic_flag_chk.bin
Pues ya está, ya hemos completado el archivo bat. En este caso lo llamaremos make2.bat para diferenciarlo del make.bat en alto nivel.
make2.bat
Código: Seleccionar todo
rem SjAsmPlus loader.asm
rem SjAsmPlus manic.asm
GenTape manic2.tzx ^
basic 'ManicMiner' 10 loader.bin ^
turbo 2168 667 735 ^
855 1710 1980 2000 manic_flag_chk.scr ^
pilot 2168 1980 ^
pulse 2 667 735 ^
pure 855 1710 2000 manic_flag_chk.bin
Llegados aquí ya estamos listos para empezar a tunear la carga. Mi idea es acortar la duración lo máximo que pueda sin perder fiabilidad de carga (o en todo caso que se pierda muy poca). Para ello copiamos el make2.bat en un make3.bat y vamos cambiando los valores numéricos. Os recomiendo que probéis vosotros mismos: cambiáis un valor, generáis el tzx y veis si carga bien. Repetís el proceso hasta que estéis satisfechos. No se trata de conseguir grandes mejoras, tened en cuenta que seguimos con carga estándar, no hemos modificado el programa cargador. Para que os hagáis una idea, desde manic2.tzx hasta manic3.tzx hemos pasado de una duración de 3:25 a otra de 2:41.
Código: Seleccionar todo
rem SjAsmPlus loader.asm
rem SjAsmPlus manic.asm
GenTape manic3.tzx ^
basic 'ManicMiner' 10 loader.bin ^
turbo 2168 667 735 ^
600 1600 1500 0 manic_flag_chk.scr ^
pilot 2168 1500 ^
pulse 2 667 735 ^
pure 600 1600 0 manic_flag_chk.bin
Dejo como ejercicio convertir a bajo nivel el tipo "basic" y recortar los tiempos de la misma forma que hemos hecho en los dos últimos bloques. Ojo, es un bloque lógico con 2 bloques físicos, os recomiendo que uséis Tapir para dumpear los binarios (tanto la cabecera como los datos).
En la siguiente lección vamos a dar un paso más, que es modificar el cargador para tener distintos colores de borde y una carga más rápida. La carga estándar es de 1500bps, con el cargador turbo de la siguiente lección (repito basado en la misma modulación que la carga estándar) espero poder alcanzar los 3000bps, que era lo habitual en las cargas turbo de la época. No se recomienda mayor velocidad con este tipo de modulación ya que bajaría drásticamente la fiabilidad.
Nos vemos en la siguiente lección.
Pincha aquí para bajar el archivo de la lección