Com he recuperat dades en un format desconegut de la cinta magnètica

prehistòria

Com que era un amant del maquinari retro, una vegada vaig comprar un ZX Spectrum+ a un venedor del Regne Unit. Inclòs amb el propi ordinador, vaig rebre diversos cassets d'àudio amb jocs (en l'embalatge original amb instruccions), així com programes gravats en cassets sense marques especials. Sorprenentment, les dades de cassets de 40 anys es llegiven bé i vaig poder descarregar-hi gairebé tots els jocs i programes.

Com he recuperat dades en un format desconegut de la cinta magnètica

Tanmateix, en alguns cassets vaig trobar enregistraments que clarament no eren fets per l'ordinador ZX Spectrum. Sonaven completament diferent i, a diferència de les gravacions de l'ordinador esmentat, no començaven amb un carregador d'arrencada BASIC curt, que sol estar present en les gravacions de tots els programes i jocs.

Durant un temps això em perseguia: tenia moltes ganes d'esbrinar què hi havia amagat. Si poguéssiu llegir el senyal d'àudio com una seqüència de bytes, podríeu buscar caràcters o qualsevol cosa que indiqui l'origen del senyal. Una mena de retro-arqueologia.

Ara que he fet tot el camí i miro les etiquetes dels mateixos cassets, somric perquè

la resposta estava davant dels meus ulls tot el temps
A l'etiqueta del casset esquerre hi ha el nom de l'ordinador TRS-80 i just a sota el nom del fabricant: "Fabricat per Radio Shack als EUA"

(Si voleu mantenir la intriga fins al final, no passeu per sota del spoiler)

Comparació de senyals d'àudio

Primer de tot, digitalitzem les gravacions d'àudio. Podeu escoltar com sona:


I com és habitual la gravació des de l'ordinador ZX Spectrum sona:


En ambdós casos, a l'inici de l'enregistrament hi ha un anomenat to pilot - un so de la mateixa freqüència (en el primer enregistrament és molt curt <1 segon, però es pot distingir). El to pilot indica a l'ordinador que es prepari per rebre dades. Per regla general, cada ordinador només reconeix el seu "propi" to pilot per la forma del senyal i la seva freqüència.

Cal dir alguna cosa sobre la forma del senyal en si. Per exemple, al ZX Spectrum la seva forma és rectangular:

Com he recuperat dades en un format desconegut de la cinta magnètica

Quan es detecta un to pilot, el ZX Spectrum mostra barres vermelles i blaves alternes a la vora de la pantalla per indicar que el senyal ha estat reconegut. S'acaba el to pilot pols de sincronització, que indica a l'ordinador que comenci a rebre dades. Es caracteritza per una durada més curta (en comparació amb el to pilot i les dades posteriors) (vegeu la figura)

Després de rebre el pols de sincronització, l'ordinador enregistra cada pujada/caiguda del senyal, mesurant-ne la durada. Si la durada és inferior a un cert límit, el bit 1 s'escriu a la memòria, en cas contrari 0. Els bits es recullen en bytes i el procés es repeteix fins que es reben N bytes. El número N s'acostuma a treure de la capçalera del fitxer descarregat. La seqüència de càrrega és la següent:

  1. to pilot
  2. capçalera (longitud fixa), conté la mida de les dades descarregades (N), el nom i el tipus del fitxer
  3. to pilot
  4. les dades en si

Per assegurar-se que les dades es carreguen correctament, el ZX Spectrum llegeix l'anomenat byte de paritat (byte de paritat), que es calcula quan es desa un fitxer fent XOR tots els bytes de les dades escrites. Quan llegeix un fitxer, l'ordinador calcula el byte de paritat a partir de les dades rebudes i, si el resultat és diferent del desat, mostra el missatge d'error "Error de càrrega de cinta R". En sentit estricte, l'ordinador pot emetre aquest missatge abans si, en llegir, no pot reconèixer un pols (perdut o la seva durada no correspon a determinats límits)

Per tant, vegem ara com és un senyal desconegut:

Com he recuperat dades en un format desconegut de la cinta magnètica

Aquest és el to pilot. La forma del senyal és significativament diferent, però és evident que el senyal consisteix a repetir polsos curts d'una determinada freqüència. A una freqüència de mostreig de 44100 Hz, la distància entre els "pics" és d'aproximadament 48 mostres (que correspon a una freqüència de ~918 Hz). Recordem aquesta figura.

Vegem ara el fragment de dades:

Com he recuperat dades en un format desconegut de la cinta magnètica

Si mesurem la distància entre polsos individuals, resulta que la distància entre polsos "llargs" encara és de ~ 48 mostres, i entre les curtes - ~ 24. Mirant una mica endavant, diré que al final va resultar que els polsos “de referència” amb una freqüència de 918 Hz segueixen contínuament, des del principi fins al final del fitxer. Es pot suposar que quan es transmeten dades, si es troba un pols addicional entre els polsos de referència, el considerem com el bit 1, en cas contrari 0.

Què passa amb el pols de sincronització? Vegem l'inici de les dades:

Com he recuperat dades en un format desconegut de la cinta magnètica

El to pilot acaba i les dades comencen immediatament. Una mica més tard, després d'analitzar diverses gravacions d'àudio diferents, vam poder descobrir que el primer byte de dades és sempre el mateix (10100101b, A5h). L'ordinador pot començar a llegir dades després de rebre-les.

També podeu parar atenció al desplaçament del primer pols de referència immediatament després de l'últim 1r del byte de sincronització. Es va descobrir molt més tard en el procés de desenvolupament d'un programa de reconeixement de dades, quan les dades al començament del fitxer no es podien llegir de manera estable.

Ara intentem descriure un algorisme que processarà un fitxer d'àudio i carregarà dades.

Carregant dades

Primer, mirem algunes suposicions per mantenir l'algorisme senzill:

  1. Només considerarem fitxers en format WAV;
  2. El fitxer d'àudio ha de començar amb un to pilot i no ha de contenir silenci al principi
  3. El fitxer font ha de tenir una freqüència de mostreig de 44100 Hz. En aquest cas, la distància entre els polsos de referència de 48 mostres ja està determinada i no cal calcular-la programadament;
  4. El format de mostra pot ser qualsevol (8/16 bits/coma flotant) -ja que en llegir-lo podem convertir-lo al desitjat;
  5. Suposem que el fitxer font està normalitzat per amplitud, la qual cosa hauria d'estabilitzar el resultat;

L'algorisme de lectura serà el següent:

  1. Llegim el fitxer a la memòria, alhora que convertim el format de mostra a 8 bits;
  2. Determineu la posició del primer pols a les dades d'àudio. Per fer-ho, cal calcular el nombre de la mostra amb la màxima amplitud. Per simplificar-ho, ho calcularem una vegada manualment. Desem-lo a la variable prev_pos;
  3. Afegiu 48 a la posició de l'últim pols (pos := prev_pos + 48)
  4. Com que augmentar la posició en 48 no garanteix que arribem a la posició del següent pols de referència (defectes de la cinta, funcionament inestable del mecanisme de la unitat de cinta, etc.), hem d'ajustar la posició del pols pos. Per fer-ho, agafeu un petit tros de dades (pos-8;pos+8) i trobeu-hi el valor màxim d'amplitud. La posició corresponent al màxim s'emmagatzemarà en pos. Aquí 8 = 48/6 és una constant obtinguda experimentalment, que ens garanteix que determinarem el màxim correcte i no afectarà altres impulsos que puguin estar a prop. En casos molt dolents, quan la distància entre polsos és molt inferior o superior a 48, es pot implementar una cerca forçada d'un pols, però dins de l'àmbit de l'article no ho descriuré a l'algorisme;
  5. En el pas anterior, també caldria comprovar que s'ha trobat el pols de referència. És a dir, si simplement busques el màxim, això no garanteix que l'impuls estigui present en aquest segment. En la meva última implementació del programa de lectura, comprovo la diferència entre els valors d'amplitud màxim i mínim en un segment i, si supera un cert límit, compte la presència d'un impuls. La pregunta també és què fer si no es troba el pols de referència. Hi ha 2 opcions: o les dades s'han acabat i segueix el silenci, o això s'ha de considerar un error de lectura. Tanmateix, ometrem això per simplificar l'algorisme;
  6. En el següent pas, hem de determinar la presència d'un pols de dades (bit 0 o 1), per a això prenem el mig del segment (prev_pos;pos) middle_pos igual a middle_pos := (prev_pos+pos)/2 i en algun barri de middle_pos al segment (middle_pos-8; middle_pos +8) calculem l'amplitud màxima i mínima. Si la diferència entre ells és superior a 10, escrivim el bit 1 al resultat, en cas contrari 0. 10 és una constant obtinguda experimentalment;
  7. Desa la posició actual a prev_pos (prev_pos := pos)
  8. Repetiu a partir del pas 3 fins que llegim tot el fitxer;
  9. La matriu de bits resultant s'ha de desar com un conjunt de bytes. Com que no hem tingut en compte el byte de sincronització en llegir, és possible que el nombre de bits no sigui un múltiple de 8 i també es desconeix el desplaçament de bits necessari. En la primera implementació de l'algorisme, no sabia sobre l'existència del byte de sincronització i, per tant, simplement vaig desar 8 fitxers amb diferents nombres de bits de compensació. Un d'ells contenia dades correctes. A l'algoritme final, simplement elimina tots els bits fins a A5h, la qual cosa em permet obtenir immediatament el fitxer de sortida correcte

Algoritme en Ruby, per als interessats
Vaig triar Ruby com a llenguatge per escriure el programa, perquè... Hi programo la major part del temps. L'opció no és d'alt rendiment, però la tasca de fer la velocitat de lectura el més ràpida possible no val la 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*")

Resultat

Després d'haver provat diverses variants de l'algorisme i constants, vaig tenir la sort d'obtenir alguna cosa extremadament interessant:

Com he recuperat dades en un format desconegut de la cinta magnètica

Així, a jutjar per les cadenes de caràcters, tenim un programa per traçar gràfics. Tanmateix, no hi ha paraules clau al text del programa. Totes les paraules clau estan codificades com a bytes (cada valor > 80 h). Ara hem d'esbrinar quin ordinador dels anys 80 podria desar programes en aquest format.

De fet, és molt semblant a un programa BASIC. L'ordinador ZX Spectrum emmagatzema programes en aproximadament el mateix format a la memòria i desa programes en cinta. Per si de cas, he contrastat les paraules clau taula. Tanmateix, el resultat va ser evidentment negatiu.

També vaig comprovar les paraules clau BÀSIQUES dels populars ordinadors Atari, Commodore 64 i diversos altres d'aquella època, per als quals vaig poder trobar documentació, però sense èxit: el meu coneixement dels tipus d'ordinadors retro va resultar no ser tan ampli.

Llavors vaig decidir anar-hi la llista, i llavors la meva mirada va caure en el nom del fabricant Radio Shack i l'ordinador TRS-80. Aquests són els noms que estaven escrits a les etiquetes dels cassets que hi havia a la meva taula! No coneixia aquests noms abans i no estava familiaritzat amb l'ordinador TRS-80, així que em va semblar que Radio Shack era un fabricant de cassets d'àudio com BASF, Sony o TDK, i el TRS-80 era el temps de reproducció. Perquè no?

Ordinador Tandy/Radio Shack TRS-80

És molt probable que l'enregistrament d'àudio en qüestió, que vaig posar com a exemple al principi de l'article, s'hagi fet en un ordinador com aquest:

Com he recuperat dades en un format desconegut de la cinta magnètica

Va resultar que aquest ordinador i les seves varietats (Model I/Model III/Model IV, etc.) van ser molt populars en un moment (per descomptat, no a Rússia). Cal destacar que el processador que utilitzaven també era Z80. Per a aquest ordinador es pot trobar a Internet molta informació. Als anys 80 es va distribuir informació informàtica revistes. De moment n'hi ha diversos emuladors ordinadors per a diferents plataformes.

He baixat l'emulador trs80gp i per primera vegada vaig poder veure com funcionava aquest ordinador. Per descomptat, l'ordinador no admetia la sortida en color; la resolució de la pantalla només era de 128x48 píxels, però hi havia moltes extensions i modificacions que podrien augmentar la resolució de la pantalla. També hi havia moltes opcions de sistemes operatius per a aquest ordinador i opcions per a la implementació del llenguatge BASIC (que, a diferència del ZX Spectrum, en alguns models ni tan sols es "flash" a la ROM i qualsevol opció es podia carregar des d'un disquet, igual que el propi sistema operatiu)

També vaig trobar utilitat convertir les gravacions d'àudio al format CAS, que és compatible amb emuladors, però per alguna raó no era possible llegir enregistraments dels meus cassets utilitzant-los.

Després d'haver descobert el format del fitxer CAS (que va resultar ser només una còpia bit a bit de les dades de la cinta que ja tenia a mà, excepte la capçalera amb la presència d'un byte de sincronització), vaig fer un alguns canvis al meu programa i vaig poder produir un fitxer CAS que funcionava que funcionava a l'emulador (TRS-80 Model III):

Com he recuperat dades en un format desconegut de la cinta magnètica

Vaig dissenyar l'última versió de la utilitat de conversió amb la determinació automàtica del primer pols i la distància entre polsos de referència com a paquet GEM, el codi font està disponible a Github.

Conclusió

El camí que hem recorregut va resultar ser un viatge fascinant al passat, i m'alegro que al final hagi trobat la resposta. Entre altres coses, jo:

  • Vaig descobrir el format per desar dades al ZX Spectrum i vaig estudiar les rutines ROM integrades per desar/llegir dades de cassets d'àudio
  • Em vaig familiaritzar amb l'ordinador TRS-80 i les seves varietats, vaig estudiar el sistema operatiu, vaig mirar programes de mostra i fins i tot vaig tenir l'oportunitat de fer depuració en codis de màquina (després de tot, tots els mnemotècnics Z80 em són familiars)
  • Va escriure una utilitat completa per convertir enregistraments d'àudio al format CAS, que pot llegir dades que no són reconegudes per la utilitat "oficial"

Font: www.habr.com

Afegeix comentari