Como recuperei datos nun formato descoñecido da cinta magnética

prehistoria

Sendo un amante do hardware retro, merquei unha vez un ZX Spectrum+ a un vendedor no Reino Unido. Incluído co propio ordenador, recibín varios casetes de audio con xogos (no envase orixinal con instrucións), así como programas gravados en casetes sen marcas especiais. Sorprendentemente, os datos de casetes de 40 anos podían lerse ben e puiden descargar case todos os xogos e programas deles.

Como recuperei datos nun formato descoñecido da cinta magnética

Non obstante, nalgúns casetes atopei gravacións que claramente non foron feitas polo ordenador ZX Spectrum. Soaban completamente diferentes e, a diferenza das gravacións do mencionado ordenador, non comezaban cun pequeno cargador de arranque BASIC, que adoita estar presente nas gravacións de todos os programas e xogos.

Durante algún tempo isto perseguíame: quería moito descubrir o que se agochaba neles. Se puideses ler o sinal de audio como unha secuencia de bytes, podes buscar caracteres ou calquera cousa que indique a orixe do sinal. Unha especie de retro-arqueoloxía.

Agora que fun todo o camiño e miro as etiquetas dos propios casetes, sorrí porque

a resposta estivo xusto diante dos meus ollos todo o tempo
Na etiqueta do casete esquerdo está o nome do ordenador TRS-80, e xusto debaixo do nome do fabricante: "Fabricado por Radio Shack en USA"

(Se queres manter a intriga ata o final, non pases por baixo do spoiler)

Comparación de sinais de audio

En primeiro lugar, imos dixitalizar as gravacións de audio. Podes escoitar como soa:


E como é habitual a gravación do ordenador ZX Spectrum soa:


En ambos os casos, ao comezo da gravación hai un chamado ton piloto - un son da mesma frecuencia (na primeira gravación é moi curto <1 segundo, pero é distinguible). O ton piloto indica ao ordenador que se prepare para recibir datos. Como regra xeral, cada ordenador recoñece só o seu "propio" ton piloto pola forma do sinal e a súa frecuencia.

É necesario dicir algo sobre a propia forma do sinal. Por exemplo, no ZX Spectrum a súa forma é rectangular:

Como recuperei datos nun formato descoñecido da cinta magnética

Cando se detecta un ton piloto, o ZX Spectrum mostra barras vermellas e azuis alternas no bordo da pantalla para indicar que o sinal foi recoñecido. O ton do piloto remata pulso sincronizado, que indica ao ordenador para comezar a recibir datos. Caracterízase por unha duración máis curta (en comparación co ton piloto e os datos posteriores) (ver figura)

Despois de recibir o pulso de sincronización, o ordenador rexistra cada subida/descenso do sinal, medindo a súa duración. Se a duración é inferior a un determinado límite, o bit 1 escríbese na memoria, noutro caso 0. Os bits recóllense en bytes e o proceso repítese ata que se reciben N bytes. O número N adoita tomarse da cabeceira do ficheiro descargado. A secuencia de carga é a seguinte:

  1. ton piloto
  2. cabeceira (longitude fixa), contén o tamaño dos datos descargados (N), o nome e o tipo de ficheiro
  3. ton piloto
  4. os propios datos

Para asegurarse de que os datos se cargan correctamente, o ZX Spectrum le o chamado byte de paridade (byte de paridade), que se calcula ao gardar un ficheiro XORing todos os bytes dos datos escritos. Ao ler un ficheiro, o ordenador calcula o byte de paridade a partir dos datos recibidos e, se o resultado é diferente do gardado, mostra a mensaxe de erro "Erro de carga de cinta R". En rigor, o ordenador pode emitir esta mensaxe antes se, ao ler, non pode recoñecer un pulso (perdida ou a súa duración non corresponde a determinados límites)

Entón, vexamos agora como é un sinal descoñecido:

Como recuperei datos nun formato descoñecido da cinta magnética

Este é o ton piloto. A forma do sinal é significativamente diferente, pero está claro que o sinal consiste en repetir pulsos curtos dunha determinada frecuencia. A unha frecuencia de mostraxe de 44100 Hz, a distancia entre os "picos" é de aproximadamente 48 mostras (o que corresponde a unha frecuencia de ~918 Hz). Lembremos esta figura.

Vexamos agora o fragmento de datos:

Como recuperei datos nun formato descoñecido da cinta magnética

Se medimos a distancia entre os pulsos individuais, resulta que a distancia entre os pulsos "longos" aínda é de ~ 48 mostras, e entre as curtas - ~ 24. Mirando un pouco cara adiante, direi que ao final resultou que os pulsos de “referencia” cunha frecuencia de 918 Hz seguen continuamente, dende o principio ata o final do ficheiro. Pódese supoñer que ao transmitir datos, se se atopa un pulso adicional entre os pulsos de referencia, considerámolo como o bit 1, noutro caso 0.

E o pulso de sincronización? Vexamos o inicio dos datos:

Como recuperei datos nun formato descoñecido da cinta magnética

O ton piloto remata e os datos comezan inmediatamente. Un pouco máis tarde, tras analizar varias gravacións de audio diferentes, puidemos descubrir que o primeiro byte de datos é sempre o mesmo (10100101b, A5h). O ordenador pode comezar a ler datos despois de que os reciba.

Tamén pode prestar atención ao desprazamento do primeiro pulso de referencia inmediatamente despois do último 1 do byte de sincronización. Descubriuse moito máis tarde no proceso de desenvolvemento dun programa de recoñecemento de datos, cando os datos ao comezo do ficheiro non se podían ler de forma estable.

Agora imos tentar describir un algoritmo que procesará un ficheiro de audio e cargará datos.

Cargando datos

Primeiro, vexamos algunhas suposicións para manter o algoritmo sinxelo:

  1. Só teremos en conta os ficheiros en formato WAV;
  2. O ficheiro de audio debe comezar cun ton piloto e non debe conter silencio ao principio
  3. O ficheiro de orixe debe ter unha frecuencia de mostraxe de 44100 Hz. Neste caso, a distancia entre os pulsos de referencia de 48 mostras xa está determinada e non necesitamos calculala programadamente;
  4. O formato de mostra pode ser calquera (8/16 bits/coma flotante) -xa que ao ler podemos convertelo ao desexado;
  5. Supoñemos que o ficheiro fonte está normalizado pola amplitude, o que debería estabilizar o resultado;

O algoritmo de lectura será o seguinte:

  1. Lemos o ficheiro na memoria, ao mesmo tempo convertendo o formato de mostra a 8 bits;
  2. Determine a posición do primeiro pulso nos datos de audio. Para iso, cómpre calcular o número de mostra coa amplitude máxima. Para simplificar, calcularémolo unha vez manualmente. Gardémolo na variable prev_pos;
  3. Engade 48 á posición do último pulso (pos := prev_pos + 48)
  4. Dado que aumentar a posición en 48 non garante que imos chegar á posición do seguinte pulso de referencia (defectos na cinta, funcionamento inestable do mecanismo de unidade de cinta, etc.), necesitamos axustar a posición do pulso pos. Para iso, colle un pequeno anaco de datos (pos-8;pos+8) e atopa o valor máximo de amplitude nel. A posición correspondente ao máximo almacenarase en pos. Aquí 8 = 48/6 é unha constante obtida experimentalmente, o que garante que determinaremos o máximo correcto e non afectará a outros impulsos que poidan estar preto. En casos moi malos, cando a distancia entre os pulsos é moito menor ou maior que 48, pódese implementar unha busca forzada dun pulso, pero no ámbito do artigo non o describirei no algoritmo;
  5. No paso anterior, tamén sería necesario comprobar que se atopou o pulso de referencia. É dicir, se simplemente busca o máximo, isto non garante que o impulso estea presente neste segmento. Na miña última implementación do programa de lectura, comprobo a diferenza entre os valores de amplitude máximo e mínimo nun segmento e, se supera un determinado límite, conto a presenza dun impulso. A pregunta tamén é que facer se non se atopa o pulso de referencia. Hai 2 opcións: ou os datos remataron e segue o silencio, ou isto debería considerarse un erro de lectura. Non obstante, omitiremos isto para simplificar o algoritmo;
  6. No seguinte paso, necesitamos determinar a presenza dun pulso de datos (bit 0 ou 1), para iso tomamos o medio do segmento (prev_pos;pos) middle_pos igual a middle_pos := (prev_pos+pos)/2 e nalgún barrio de middle_pos no segmento (middle_pos-8; middle_pos +8) calculemos a amplitude máxima e mínima. Se a diferenza entre eles é superior a 10, escribimos o bit 1 no resultado, senón 0. 10 é unha constante obtida experimentalmente;
  7. Garda a posición actual en prev_pos (prev_pos := pos)
  8. Repita a partir do paso 3 ata que lemos todo o ficheiro;
  9. A matriz de bits resultante debe gardarse como un conxunto de bytes. Dado que non tivemos en conta o byte de sincronización ao ler, é posible que o número de bits non sexa un múltiplo de 8 e tamén se descoñece a compensación de bits necesaria. Na primeira implementación do algoritmo, non sabía sobre a existencia do byte de sincronización e, polo tanto, simplemente gardaba 8 ficheiros con diferentes números de bits de compensación. Un deles contiña datos correctos. No algoritmo final, simplemente quito todos os bits ata A5h, o que me permite obter inmediatamente o ficheiro de saída correcto

Algoritmo en Ruby, para os interesados
Escollín Ruby como linguaxe para escribir o programa, porque... Programo nela a maior parte do tempo. A opción non é de altas prestacións, pero a tarefa de facer que a velocidade de lectura sexa o máis rápida posible non paga a pena.

# Используем 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*")

Resultado

Despois de probar varias variantes do algoritmo e das constantes, tiven a sorte de conseguir algo moi interesante:

Como recuperei datos nun formato descoñecido da cinta magnética

Entón, a xulgar polas cadeas de caracteres, temos un programa para trazar gráficos. Non obstante, non hai palabras clave no texto do programa. Todas as palabras clave están codificadas como bytes (cada valor > 80 h). Agora temos que descubrir que ordenador dos 80 podería gardar programas neste formato.

De feito, é moi semellante a un programa BASIC. O ordenador ZX Spectrum almacena programas aproximadamente no mesmo formato na memoria e garda os programas en cinta. Por se acaso, comprobei as palabras clave táboa. Non obstante, o resultado foi obviamente negativo.

Tamén comprobei as palabras clave BÁSICAS dos populares Atari, Commodore 64 e varios outros ordenadores daquela época, para os que puiden atopar documentación, pero sen éxito: os meus coñecementos sobre os tipos de ordenadores retro resultaron non ser tan amplos.

Entón decidín ir a lista, e entón a miña mirada caeu no nome do fabricante Radio Shack e no ordenador TRS-80. Estes son os nomes que estaban escritos nas etiquetas dos casetes que estaban sobre a miña mesa! Non coñecía estes nomes antes e non estaba familiarizado co ordenador TRS-80, polo que pareceume que Radio Shack era un fabricante de casetes de audio como BASF, Sony ou TDK, e o TRS-80 era o tempo de reprodución. Por que non?

Ordenador Tandy/Radio Shack TRS-80

É moi probable que a gravación de audio en cuestión, que puxen como exemplo ao comezo do artigo, se fixera nun ordenador coma este:

Como recuperei datos nun formato descoñecido da cinta magnética

Resultou que este ordenador e as súas variedades (Modelo I/Modelo III/Modelo IV, etc.) foron moi populares nun momento (por suposto, non en Rusia). Cabe destacar que o procesador que empregaron tamén era o Z80. Para este ordenador podes atopar en Internet moita información. Nos anos 80 distribuíuse a información informática revistas. Polo momento son varias emuladores ordenadores para diferentes plataformas.

Descarguei o emulador trs80gp e por primeira vez puiden ver como funcionaba este ordenador. Por suposto, o ordenador non admitía a saída en cor; a resolución da pantalla era só de 128 x 48 píxeles, pero houbo moitas extensións e modificacións que poderían aumentar a resolución da pantalla. Tamén había moitas opcións de sistemas operativos para este ordenador e opcións de implementación da linguaxe BASIC (que, a diferenza do ZX Spectrum, nalgúns modelos nin sequera se "flasheaba" na ROM e calquera opción podía cargarse desde un disquete, igual que o propio sistema operativo)

Eu tamén atopei utilidade para converter as gravacións de audio ao formato CAS, que é compatible con emuladores, pero por algún motivo non foi posible ler as gravacións dos meus casetes usándoas.

Despois de descubrir o formato do ficheiro CAS (que resultou ser só unha copia pouco a pouco dos datos da cinta que xa tiña a man, excepto a cabeceira coa presenza dun byte de sincronización), fixen un algúns cambios no meu programa e puiden sacar un ficheiro CAS que funcionou no emulador (TRS-80 Model III):

Como recuperei datos nun formato descoñecido da cinta magnética

Deseñei a última versión da utilidade de conversión coa determinación automática do primeiro pulso e da distancia entre os pulsos de referencia como paquete GEM, o código fonte está dispoñible en Github.

Conclusión

O camiño que percorremos resultou ser unha viaxe fascinante ao pasado, e alégrome de que ao final atopei a resposta. Entre outras cousas, eu:

  • Descubrín o formato para gardar datos no ZX Spectrum e estudei as rutinas ROM integradas para gardar/ler datos de casetes de audio
  • Coñecín a computadora TRS-80 e as súas variedades, estudei o sistema operativo, mirei programas de mostra e mesmo tiven a oportunidade de facer depuración en códigos de máquina (ao final, todos os mnemotécnicos Z80 son familiares para min)
  • Escribiu unha utilidade completa para converter gravacións de audio ao formato CAS, que pode ler datos que non son recoñecidos pola utilidade "oficial"

Fonte: www.habr.com

Engadir un comentario