Empecemos con la de nivel usuario...
Estaba pasando el rato con el emulador fuse, haciendo chorraditas que pretendia grabar en una cinta en formato tap y me encuentro con una curiosa circunstancia.
Para grabar en una cinta lo que hago es crear un fichero vacío y luego grabar con comandos save del spectrum, generando bloques en la cinta.
El primer problema es obtener el fichero vacío inicial, si uso el menú Media > Tape > Write y le indico un nombre de fichero inexistente, da un montón de errores, recorriendo los distintos tipos de bloques que reconoce fuse, porque al ser una cinta vacia no hay bloques que grabar (se entiende que no hay cinta insertada). Si elegimos entonces el camino obvio y primero insertamos una cinta mediante el menú Media > Tape > Open y le indico un fichero no existente (para que lo cree) nos indica que no existe el fichero, da un error y no crea nada (otros emuladores sí crean un fichero si le indicas uno que no existe)
Entonces solo queda una solución, o partes de un fichero tap existente y lo copias en un nuevo fichero, a modo de template, o creas un fichero de 0 bytes con extensión .tap (fuse deduce el tipo de formato de la extensión del fichero)
En mi caso, creo un fichero de 0 bytes pues quiero una cinta vacía, por ejemplo z.tap, lo abro con Media > Tape > Open y a partir de ahí tengo una cinta insertada en el emulador. Media > Tape > Browser no muestra ningún contenido.
Creo un programilla de prueba y SAVE "p" <enter>, voy a Media > Tape > Record Start y pulso una tecla, obtengo el mensaje de OK y voy a Media > Tape > Record Stop
Ahora voy a ver el contenido de la cinta con Media > Tape > Browser y veo que hay un bloque de tipo RLE Pulse.
Bien, pues ahora quiero grabar la cinta en un fichero de formato tap, así que voy a Media > Tape > Write e indico un nombre de fichero nuevo, digamos zz.tap y Fuse me da el error "libspectrum: skip_block: skipping RLE Pulse (ID 0x100); conversion almost certainly won't work"
Este error tiene sentido pues hasta donde yo sé los ficheros tap usan bloques ROM estándar y no bloques RLE. Pese al error me genera el fichero zz.tap con tamaño 0. Por lo tanto he perdido mi programilla.
Pruebo entonces a guardar la cinta en formato txz en lugar de tap, por el mismo procedimiento, solo que indicando como fichero zz.tzx y entonces obtengo un fichero zz.tzx de 57 kb y si lo abro desde fuse puedo cargar mi programa.
Sin embargo, cuando cargo la cinta zz.tzx mediante Media > Tape > Open y miro su contenido con Media > Tape > Browser veo que contiene un bloque de tipo Raw Data de 57835 bytes.
Recapitulando, si abro un fichero .tap vacio como cinta insertada y grabo un programa, obtengo en la cinta insertada un bloque RLE Pulse (no indica tamaño) que no puedo escribir (Write) en un fichero .tap pero sí en un fichero .tzx que si luego abro como cinta insertada me encuentro que contiene un bloque Raw Data de 57kb.
La pregunta obvia es como hacer para crear una cinta de cero y guardarla en formato .tap en Fuse, la menos obvia es para qué guarda Fuse los bloques grabados en cinta en un formato que no puede escribir, no tiene mucho sentido.
La mayoria de ficheros .tap (todos hasta donde yo sé) contienen bloques ROM estándar y de hecho si abres en Fuse un tap generado por otro emulador (por ej el fantástico zesarux) y miras el contenido te encuentras bloques del tipo Standard Speed Data.
Entiendo que Fuse debería guardar los bloques en ese formato también y si no sabe qué formato usar pues todavía no has hecho un Write a un formato concreto, al menos debería poder convertir sus bloques RLE internos al tipo de bloque esperado por el formato destino.
El caso es que debido a esto Fuse no puede guardar archivos .tap desde bloques de cinta que has ido SAVEando (salvo que haga algo mal que es posible), pero bueno, al menos sí puede guardar archivos .tzx no?
Pues el caso es que sí pero no, me explico, el formato de bloques que guarda en el archivo .txz es muy particular pues utiliza un tipo de bloque soportado por el formato tzx que hace prácticamente imposible convertir el tzx generado a formato tap con alguna de las utilidades existentes.
y con esto llegamos a la pregunta técnica...
Fuse graba los bloques guardados en cinta como bloques RLE y cuando los guarda en formato tzx los guarda como bloques "Raw Data" en terminología Fuse, que son los bloque de tipo ID 15 - Direct Recording definidos en el formato tzx
Estos bloques tienen la siguiente estructura [1]:
Código: Seleccionar todo
length: [05,06,07]+08
Offset Value Type Description
0x00 - WORD Number of T-states per sample (bit of data)
0x02 - WORD Pause after this block in milliseconds (ms.)
0x04 - BYTE Used bits (samples) in last byte of data (1-8) (e.g. if this is 2, only first two samples of the last byte will be played)
0x05 N BYTE[3] Length of samples
0x08 - BYTE[N] Samples data. Each bit represents a state on the EAR port (i.e. one sample). MSb is played first.
Como un archivo TAP contiene bloques estándar, todos los programas de conversión de tzx a tap que conozco se saltan los bloques que no son estándar y como resultado obtienes en el mejor de los casos un archivo tap de 0 bytes como resultado de la conversión y en el peor un error.
La pregunta es ¿sería posible convertir un bloque 15 en una secuencia de bloques estándar para escribir en un archivo tap?
El bloque 15 contiene los samples generados por EAR, es decir una reproducción "analógica" de la cinta tal cual la grabaría un spectrum real, mientras que según entiendo, un archivo .tap tiene los bytes de la cinta tal y como los leería el spectrum, información digital.
Entonces para convertir el bloque 15 en bloques estandar, habría que ir leyendo cada grupo de "8 samples" e ir convirtiendolo a un byte con el valor apropiado. No recuerdo ahora mismo el formato de grabación en cinta que usa el spectrum pero seguramente un byte no serán exactamente 8 samples porque quizá haya información de paridad, sincronismo, etc
Entiendo que a efectos de una conversión a tap, los dos primeros campos del bloque (número de t-states por sample y pausa en ms despues del bloque) no tienen efecto, porque en el campo de datos ya viene toda la secuencia de samples sin importar la duración ni la frecuencia de cada sample, quizá estos valores sean importantes para un emulador pero no para una utilidad de conversión.
El cuarto campo, bits en el ultimo byte de datos, sí parece importante porque al leer la secuencia de samples puedes no tener un multiplo de 8 samples, 8 bits, sino que el ultimo byte puede tener menos samples (bits), en este caso no tengo muy claro como se interpreta su valor, imagino que serán los menos significativos.
Por tanto un posible algoritmo de conversión en pseudoC (uso los campos sin prefijarlos con el nombre de la estructura donde se definen por claridad) sería algo así:
Código: Seleccionar todo
TAP_block = RECORD {byte size[2]; byte flag; byte DATA[N]}
TZX_block = RECORD { byte tstates[2]; ...; byte bits_last_byte; byte length_samples[3]; byte SAMPLES[N]}
memcpy(DATA,SAMPLES,length_samples-1)
byte b=SAMPLES[length_samples-1]
DATA[length_samples-1] = b >> bits_last_byte
flag=FF
size=length_samples
El problema es que te puedes encontres con bloques de samples más largos que el tamaño de bloque de un tap (2 bytes para el tamaño de bloque tap, 3 bytes para el tamaño de samples tzx)
¿estoy muy equivocado o van por ahí los tiros y la cosa es factible?