Cómo recuperé datos en un formato desconocido de una cinta magnética

Antecedentes



Como amante del hierro retro, una vez compré un ZX Spectrum + de un vendedor del Reino Unido. Completo con la computadora en sí, obtuve varios cassettes de audio con juegos (en el paquete original con instrucciones), así como programas grabados en cassettes sin ninguna designación especial. Sorprendentemente, los datos de los cassettes de 40 años eran legibles y pude descargar casi todos los juegos y programas de ellos.







Sin embargo, en algunos casetes encontré grabaciones que claramente no fueron hechas por la computadora ZX Spectrum. Sonaban completamente diferentes y, a diferencia de las grabaciones de la computadora antes mencionada, no comenzaron con un cargador de arranque BASIC corto, que generalmente está presente en las grabaciones de todos los programas y juegos.



Durante algún tiempo fui perseguido por esto, realmente quería saber qué estaba oculto en ellos. Si pudiera leer la señal de audio como una secuencia de bytes, podría buscar en ellos caracteres o algo que indique el origen de la señal. Una especie de arqueología retro.



Ahora que he recorrido todo el camino y miro las etiquetas de los casetes, sonrío porque

la respuesta estaba justo en frente de mis ojos todo este tiempo
— TRS-80, : «Manufactured by Radio Shack in USA»


(Si quieres mantener la intriga hasta el final, no te metas debajo del spoiler)



Comparación de señales de audio.



El primer paso es digitalizar grabaciones de audio. Puedes escuchar cómo suena:





Y como de costumbre suena la grabación de la computadora ZX Spectrum:





En ambos casos, al comienzo de la grabación, hay un llamado tono piloto : un sonido de una frecuencia (en la primera grabación es muy corto <1 segundo, pero distinguible). El tono piloto le indica a la computadora que se prepare para recibir datos. Como regla general, cada computadora reconoce solo "su" tono piloto por la forma de onda y su frecuencia.



Debo decir sobre la forma de la señal en sí. Por ejemplo, en el ZX Spectrum, su forma es rectangular:







cuando se detecta un tono piloto, el ZX Spectrum muestra franjas rojas y azules alternas en el borde de la pantalla, lo que indica que se reconoce la señal. El tono piloto termina con un pulso sincronizado, que le indica a la computadora que comience a recibir datos. Se caracteriza por una duración más corta (en comparación con el tono piloto y los datos subsiguientes) (ver figura)



Después de recibir el pulso de sincronización, la computadora registra cada aumento / caída de la señal, midiendo su duración. Si la duración es inferior a un cierto límite, el bit 1 se escribe en la memoria, de lo contrario, 0. Los bits se recopilan en bytes y el proceso se repite hasta que se reciben N bytes. El número N generalmente se toma del encabezado del archivo descargado. La secuencia de arranque es la siguiente:



  1. tono piloto
  2. encabezado (longitud fija), contiene el tamaño de los datos cargados (N), nombre y tipo de archivo
  3. tono piloto
  4. los datos en sí


Para asegurarse de que los datos se carguen correctamente, ZX Spectrum lee el último byte de la llamada paridad de bytes (byte de paridad), que se calcula al guardar la operación XOR de archivo sobre todos los bytes de los datos grabados. Al leer el archivo, la computadora calcula el byte de paridad a partir de los datos recibidos y, si el resultado difiere del guardado, muestra el mensaje de error "R Error al cargar la cinta". Hablando estrictamente, la computadora puede emitir este mensaje antes si, al leer, no puede reconocer el impulso (se pierde o su duración no corresponde a ciertos límites)



Entonces, ahora veamos cómo se ve una señal desconocida:







Este es un tono piloto. La forma de onda es significativamente diferente, pero puede ver que la señal consiste en pulsos cortos repetitivos de cierta frecuencia. A una frecuencia de muestreo de 44100 Hz, la distancia entre los "picos" es de aproximadamente 48 muestras (que corresponde a una frecuencia de ~ 918 Hz). Recordemos esta cifra.



Ahora veamos el fragmento con los datos:







si medimos la distancia entre pulsos individuales, resulta que la distancia entre pulsos "largos" sigue siendo ~ 48 muestras, y entre los cortos - ~ 24 muestras. Avanzando un poco más adelante, diré que al final resultó que los pulsos de "referencia" con una frecuencia de 918 Hz siguen continuamente, desde el principio hasta el final del archivo. Se puede suponer que durante la transmisión de datos, si se produce un pulso adicional entre los pulsos de referencia, lo consideramos como bit 1, de lo contrario 0.



¿Qué pasa con el pulso sincronizado? Veamos el comienzo de los datos:







el tono piloto finaliza y los datos comienzan de inmediato. Un poco más tarde, después de analizar varias grabaciones de audio diferentes, descubrimos que el primer byte de datos siempre es el mismo (10100101b, A5h). La computadora puede comenzar a leer datos después de recibirlos.



También puede prestar atención al cambio del primer pulso de referencia inmediatamente después del último 1 en el sincronizador. Se descubrió mucho más tarde en el proceso de desarrollo de un programa para el reconocimiento de datos, cuando los datos al comienzo del archivo no podían leerse de manera estable.



Ahora intentemos describir un algoritmo que procesará un archivo de audio y cargará datos.



Cargando datos



Primero, veamos algunos supuestos para no complicar el algoritmo:



  1. Consideraremos los archivos solo en formato WAV;
  2. El archivo de audio debe comenzar con un tono piloto y no debe contener silencio al principio
  3. El archivo fuente debe tener una frecuencia de muestreo de 44100 Hz. En este caso, la distancia entre los pulsos de referencia de 48 muestras ya ha sido determinada y no necesitamos calcularla programáticamente;
  4. El formato de muestra puede ser cualquier (8/16 bit / punto flotante), ya que al leer, podemos convertirlo al deseado;
  5. Suponemos que el archivo original está normalizado en amplitud, lo que debería estabilizar el resultado;


El algoritmo de lectura será el siguiente:



  1. Leemos el archivo en la memoria, al mismo tiempo convertimos el formato de muestra a 8 bits;
  2. Determine la posición del primer pulso en los datos de audio. Para hacer esto, necesita calcular el número de la muestra con la amplitud máxima. Para simplificar, contémoslo manualmente una vez. Vamos a guardarlo en la variable prev_pos;
  3. Agregue 48 a la posición del último impulso (pos: = prev_pos + 48)
  4. 48 , ( , ), pos. (pos-8;pos+8) . , , pos. 8 = 48/6 — , , . , 48, , ;
  5. , . , , . , , . , . 2 : , . ;
  6. ( 0 1), (prev_pos;pos) middle_pos middle_pos := (prev_pos+pos)/2 middle_pos (middle_pos-8;middle_pos+8) . 10, 1 0. 10 — ;
  7. prev_pos (prev_pos := pos)
  8. 3, ;
  9. . - , 8, . - 8 . . A5h,


Ruby,
Ruby, .. . , .



#  gem 'wavefile'
require 'wavefile'

reader = WaveFile::Reader.new('input.wav')
samples = []
format = WaveFile::Format.new(:mono, :pcm_8, 44100)

#  WAV ,    Mono, 8 bit 
#  samples       0-255
reader.each_buffer(10000) do |buffer|
  samples += buffer.convert(format).samples
end

#    ( 0)
prev_pos = 0
#   
distance = 48
#       
delta = (distance / 6).floor
#        "0"  "1"
bits = ""

loop do
  #    
  pos = prev_pos + distance
  
  #       
  break if pos + delta >= samples.size

  #   pos     [pos - delta;pos + delta]
  (pos - delta..pos + delta).each { |p| pos = p if samples[p] > samples[pos] }

  #    [prev_pos;pos]
  middle_pos = ((prev_pos + pos) / 2).floor

  #     
  sample = samples[middle_pos - delta..middle_pos + delta]

  #    "1"           10
  bit = sample.max - sample.min > 10
  bits += bit ? "1" : "0"
end

#  -       256   (  ) 
bits.gsub! /^[01]*?10100101/, ("0" * 256) + "10100101"

#   ,    
File.write "output.cas", [bits].pack("B*")






Después de probar varias variantes del algoritmo y las constantes, tuve la suerte de obtener algo extremadamente interesante:







Entonces, a juzgar por las cadenas de caracteres, tenemos un programa para trazar gráficos. Sin embargo, no hay palabras clave en el texto del programa. Todas las palabras clave se codifican como bytes (cada valor> 80h). Ahora tenemos que averiguar qué computadora de los años 80 podría guardar programas en este formato.



Esto es realmente muy similar a un programa BÁSICO. En aproximadamente el mismo formato, la computadora ZX Spectrum se almacena en la memoria y guarda los programas en cinta. Por si acaso, verifiqué las palabras clave contra la tabla . Sin embargo, el resultado fue obviamente negativo.



También verifiqué las palabras clave BÁSICAS de las populares computadoras Atari, Commodore 64 y varias otras, para las cuales logré encontrar documentación, pero fue en vano; mi conocimiento de los tipos de computadoras retro no era tan amplio.



Entonces decidí revisar la lista , y luego mis ojos se posaron en el nombre del fabricante de Radio Shack y la computadora TRS-80. ¡Estos nombres fueron escritos en las etiquetas de los casetes que estaban sobre mi mesa! Después de todo, no conocía estos nombres antes y no estaba familiarizado con la computadora TRS-80, así que me pareció que Radio Shack era un fabricante de casetes de audio, como BASF, Sony o TDK, y TRS-80 era la duración de la reproducción. Por qué no?



Computadora Tandy / Radio Shack TRS-80



Es muy probable que la grabación de audio en cuestión, que di como ejemplo al comienzo del artículo, se haya realizado en una computadora de este tipo:







resultó que esta computadora y sus variantes (Modelo I / Modelo III / Modelo IV, etc.) eran muy populares en su tiempo (por supuesto, no en Rusia). Es de destacar que el procesador utilizado en ellos también es Z80. Se puede encontrar mucha información en esta computadora en Internet . En la década de 1980, la información sobre la computadora circulaba en revistas . Por el momento, hay varios emuladores de computadora para diferentes plataformas.



Descargué el emulador trs80gpy por primera vez pude ver cómo funcionaba esta computadora. Por supuesto, la computadora no era compatible con la salida de color, la resolución de la pantalla era de solo 128x48 píxeles, pero había muchas extensiones y modificaciones que podían aumentar la resolución de la pantalla. También había muchas opciones para los sistemas operativos de esta computadora y opciones para implementar el lenguaje BASIC (que, a diferencia del ZX Spectrum, en algunos modelos ni siquiera se "flasheó" en la ROM y cualquier opción se podía cargar desde un disquete, así como desde el propio sistema operativo)



. Encontré una utilidad para convertir grabaciones de audio en formato CAS, que es compatible con emuladores, pero por alguna razón no pude leer las grabaciones de mis casetes al usarlas.



Habiendo descubierto el formato del archivo CAS (que resultó ser solo una copia bit a bit de los datos de la cinta, que ya tenía en mis manos, con la excepción del encabezado con la presencia de un byte de sincronización), hice varios cambios en mi programa y pude obtener un archivo CAS que funcionaba en la salida, que funcionó en el emulador (TRS-80 Modelo III):







la última versión de la utilidad para convertir con detección automática del primer pulso y la distancia entre los pulsos de referencia que diseñé como un paquete GEM, el código fuente está disponible en Github .



Conclusión



El camino recorrido resultó ser un emocionante viaje al pasado, y me alegro de que al final encontré una solución. Entre otras cosas, yo:



  • Descubrí el formato para guardar datos en el ZX Spectrum y estudié las rutinas ROM incorporadas para guardar / leer datos de cintas de audio
  • Conocí la computadora TRS-80 y sus variedades, estudié el sistema operativo, miré ejemplos de programas e incluso tuve la oportunidad de depurar códigos de máquina (después de todo, todos los mnemónicos del Z80 me son familiares)
  • Escribí una utilidad completa para convertir grabaciones de audio al formato CAS, que puede leer datos que no son reconocidos por la utilidad "oficial"



All Articles