Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Vorgeschichte

Als Liebhaber von Retro-Hardware habe ich eines Tages einen ZX Spectrum+ von einem Verkäufer aus Großbritannien erworben. Im Paket mit dem Computer erhielt ich einige Audiokassetten mit Spielen (in der originalen Verpackung mit Anleitungen) sowie Programme, die auf Kassetten ohne besondere Kennzeichnungen aufgenommen wurden. Zu meiner Überraschung waren die Daten der 40 Jahre alten Kassetten gut lesbar, und es gelang mir, fast alle Spiele und Programme von ihnen zu laden.

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Auf einigen Kassetten entdeckte ich jedoch Aufnahmen, die eindeutig nicht von einem ZX Spectrum Computer stammten. Sie klangen völlig anders und begannen im Gegensatz zu den Aufnahmen des erwähnten Computers nicht mit einem kurzen BASIC-Ladeprogramm, das normalerweise in allen Programmen und Spielen vorhanden ist.

Eine Zeit lang ließ mich das nicht in Ruhe – ich wollte unbedingt wissen, was sich darin verbarg. Wenn es mir gelungen wäre, das Audiosignal als Byteserie zu lesen, hätte ich nach Symbolen oder etwas suchen können, das auf die Herkunft des Signals hindeutet. Eine Art Retro-Archäologie.

Jetzt, da ich den gesamten Weg zurückgelegt habe und auf die Etiketten der Kassetten schaue, lächle ich, denn

die Antwort war die ganze Zeit direkt vor meinen Augen.
Auf dem Etikett der linken Kassette steht der Name des Computers TRS-80, und etwas darunter der Hersteller: „Manufactured by Radio Shack in USA“

(Wenn Sie die Spannung bis zum Ende behalten wollen, klicken Sie nicht auf den Spoiler.)

Vergleich der Audiosignale

Zuerst digitalisieren wir die Audiowiedergaben. Man kann hören, wie es klingt:


Und so klingt eine Aufnahme vom Computer ZX Spectrum:


In beiden Fällen gibt es zu Beginn der Aufnahme einen sogenannten Pilotton – ein Ton einer Frequenz (bei der ersten Aufnahme ist er sehr kurz <1 Sek, aber er ist erkennbar). Der Pilotton dient dem Computer als Signal, sich auf den Empfang von Daten vorzubereiten. In der Regel erkennt jeder Computer nur seinen „eigenen“ Pilotton anhand der Signatur und Frequenz des Signals.

Auch zur Form des Signals muss gesagt werden. Zum Beispiel ist sie beim ZX Spectrum rechteckig:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Bei Erkennung des Pilottons zeigt der ZX Spectrum abwechselnd rote und blaue Streifen am Rand des Bildschirms an und signalisiert damit, dass das Signal erkannt wurde. Der Pilotton endet mit einem Synchronimpuls., der dem Computer signalisiert, dass er beginnen soll, Daten zu empfangen. Er zeichnet sich durch eine geringere (im Vergleich zum Pilotton und den folgenden Daten) Dauer aus (siehe Abbildung)

Nachdem der Synchronimpuls empfangen wurde, registriert der Computer jeden Anstieg/Abfall des Signals und misst dessen Dauer. Wenn die Dauer unter einer festgelegten Grenze liegt, wird ein Bit 1 in den Speicher geschrieben, andernfalls 0. Die Bits werden zu Bytes gesammelt, und der Prozess wird wiederholt, bis N Bytes empfangen wurden. Die Zahl N wird in der Regel aus dem Header der heruntergeladenen Datei entnommen. Die Reihenfolge des Ladevorgangs ist folgende:

  1. Pilotton
  2. Header (fester Länge), enthält die Größe der herunterzuladenden Daten (N), Namen und Typ der Datei
  3. Pilotton
  4. die eigentlichen Daten

Um sicherzustellen, dass die Daten korrekt geladen wurden, liest das ZX Spectrum das sogenannte Paritätsbyte , das beim Speichern der Datei durch eine XOR-Operation über alle Bytes der gespeicherten Daten berechnet wird. Beim Lesen der Datei berechnet der Computer das Paritätsbyte aus den empfangenen Daten, und wenn das Ergebnis vom gespeicherten abweicht, gibt er eine Fehlermeldung „R Tape loading error“ aus. Streng genommen kann der Computer diese Meldung auch früher ausgeben, wenn er beim Lesen den Impuls nicht erkennen kann (entweder fehlt er oder seine Dauer entspricht nicht bestimmten Grenzen)

Schauen wir uns nun an, wie das unbekannte Signal aussieht:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Das ist der Pilotton. Die Form des Signals unterscheidet sich erheblich, aber man sieht, dass das Signal aus wiederholten kurzen Impulsen einer bestimmten Frequenz besteht. Bei einer Abtastrate von 44100 Hz beträgt der Abstand zwischen den „Spitzen“ etwa 48 Samples (was einer Frequenz von ~918 Hz entspricht). Merken wir uns diese Zahl.

Schauen wir uns nun einen Datenabschnitt an:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Wenn man den Abstand zwischen den einzelnen Impulsen misst, stellt man fest, dass der Abstand zwischen den „langen“ Impulsen immer noch ungefähr 48 Samples beträgt, während zwischen den kurzen etwa 24 sind. Um nicht zu weit vorzugreifen, sage ich, dass sich herausgestellt hat, dass die „Referenz“-Impulse mit einer Frequenz von 918 Hz kontinuierlich von Anfang bis Ende der Datei folgen. Man könnte annehmen, dass bei der Datenübertragung, wenn zwischen den Referenzimpulsen ein zusätzlicher Impuls erscheint, wir ihn als Bit 1 zählen, andernfalls als 0.

Was ist mit dem Synchronimpuls? Schauen wir uns den Beginn der Daten an:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Der Pilotton endet und sofort beginnen die Daten. Nach der Analyse mehrerer unterschiedlicher Audioaufzeichnungen stellte sich heraus, dass das erste Datenbyte immer dasselbe ist (10100101b, A5h). Möglicherweise beginnt der Computer, die Daten zu lesen, nachdem er diese erhalten hat.

Man kann auch auf die Verschiebung des ersten Referenzimpulses unmittelbar nach der letzten 1 im Synchronisationsbyte achten. Dieser wurde während der Entwicklung des Programms zur Datenkennung deutlich später entdeckt, als die Daten zu Beginn der Datei nicht stabil gelesen werden konnten.

Jetzt versuchen wir, den Algorithmus zu beschreiben, der die Audiodatei verarbeitet und die Daten lädt.

Daten hochladen

Zunächst betrachten wir einige Annahmen, um den Algorithmus nicht zu verkomplizieren:

  1. Wir betrachten nur Dateien im WAV-Format;
  2. Die Audiodatei muss mit einem Pilotton beginnen und darf zu Beginn keine Stille enthalten.
  3. Die Ausgangsdatei muss eine Abtastrate von 44100 Hz haben. In diesem Fall ist der Abstand zwischen den Referenzimpulsen von 48 Samples bereits definiert und wir müssen ihn nicht programmiert berechnen;
  4. Das Sample-Format kann beliebig sein (8/16 Bit/mit Gleitkomma), da wir es beim Lesen in das benötigte umwandeln können;
  5. Wir gehen davon aus, dass die Ausgangsdatei in der Amplitude normalisiert ist, was das Ergebnis stabilisieren sollte;

Der Lesealgorithmus wird wie folgt aussehen:

  1. Wir lesen die Datei in den Speicher und konvertieren gleichzeitig das Sample-Format in 8 Bit;
  2. Wir bestimmen die Position des ersten Impulses in den Audiodaten. Zu diesem Zweck muss die Sample-Nummer mit der maximalen Amplitude berechnet werden. Zur Vereinfachung zählen wir ihn einmal manuell. Wir speichern dies in der Variable prev_pos;
  3. Wir addieren 48 zur Position des letzten Impulses (pos := prev_pos + 48)
  4. Da eine Erhöhung der Position um 48 nicht garantiert, dass wir in die Position des nächsten Referenzimpulses gelangen (Bandfehler, instabile Funktionsweise des Bandantriebs und anderes), muss die Impulsposition pos korrigiert werden. Dazu nehmen wir einen kleinen Datenbereich (pos-8;pos+8) und finden das Maximum des Amplitudenwertes darin. Die Position, die dem Maximum entspricht, speichern wir in pos. Hierbei ist 8 = 48/6 - eine experimentell bestimmte Konstante, die garantiert, dass wir das korrekte Maximum ermitteln und andere Impulse, die möglicherweise in der Nähe liegen, nicht beeinträchtigen. In sehr schlechten Fällen, in denen der Abstand zwischen den Impulsen deutlich kleiner oder größer als 48 ist, könnte eine erzwungene Impulsfindung realisiert werden, aber im Rahmen dieses Artikels werde ich dies im Algorithmus nicht beschreiben;
  5. Im vorherigen Schritt muss auch überprüft werden, ob der Referenzimpuls überhaupt gefunden wurde. Das heißt, nur das Maximum zu suchen, garantiert nicht, dass der Impuls in diesem Abschnitt vorhanden ist. In meiner letzten Implementierung des Leseprogramms überprüfe ich den Unterschied zwischen dem maximalen und minimalen Amplitudenwert im Abschnitt, und wenn dieser einen bestimmten Schwellenwert überschreitet, zähle ich das Vorhandensein eines Impulses. Die Frage ist auch, was zu tun ist, wenn der Referenzimpuls nicht gefunden wird. Hier gibt es zwei Optionen: Entweder sind die Daten zu Ende und es folgt eine Stille, oder dies sollte als Lesefehler betrachtet werden. Wir lassen dies jedoch zur Vereinfachung des Algorithmus weg;
  6. Im nächsten Schritt muss das Vorhandensein eines Datenimpulses (Bit 0 oder 1) bestimmt werden, dafür nehmen wir die Mitte des Abschnitts (prev_pos;pos) middle_pos gleich middle_pos := (prev_pos+pos)/2 und in einem gewissen Bereich um middle_pos herum im Abschnitt (middle_pos-8;middle_pos+8) zählen wir das Maximum und Minimum der Amplitude. Wenn der Unterschied zwischen ihnen mehr als 10 beträgt, schreiben wir das Bit 1 in das Ergebnis, andernfalls 0. 10 ist eine durch Erfahrung gewonnene Konstante;
  7. Wir speichern die aktuelle Position in prev_pos (prev_pos := pos)
  8. Wir wiederholen ab Schritt 3, bis die gesamte Datei gelesen ist;
  9. Das erhaltene Bit-Array muss als Byte-Array gespeichert werden. Da wir beim Lesen das Synchronisationsbyte nicht berücksichtigt haben, kann sich die Anzahl der Bits als nicht durch 8 teilbar herausstellen, und das notwendige Offset in Bits ist ebenfalls unbekannt. In der ersten Implementierung des Algorithmus wusste ich nichts über die Existenz des Synchronisationsbytes und speicherte einfach 8 Dateien mit unterschiedlichen Bit-Offets. Eine von ihnen enthielt korrekte Daten. Im endgültigen Algorithmus entferne ich einfach alle Bits bis A5h, was es mir ermöglicht, sofort eine korrekte Datei auszugeben.

Algorithmus in Ruby, für Interessierte
Als Programmiersprache wählte ich Ruby, da ich die meiste Zeit damit programmiere. Diese Variante ist nicht sehr leistungsfähig, aber das Ziel, die Lesegeschwindigkeit maximal zu erhöhen, ist nicht gegeben.

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

Ergebnis

Nachdem ich mehrere Varianten des Algorithmus und Konstanten ausprobiert habe, hatte ich das Glück, etwas äußerst Interessantes zu erhalten:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Also, anhand der Zeichenfolgen haben wir ein Programm zur Erstellung von Diagrammen. Allerdings fehlen im Programmtext die Schlüsselwörter. Alle Schlüsselwörter sind in Form von Bytes kodiert (Wert jedes > 80h). Jetzt muss herausgefunden werden, welcher Computer aus den 80ern Programme in diesem Format speichern konnte.

Tatsächlich ähnelt es sehr einem Programm in der Sprache BASIC. In einem etwa ähnlichen Format speichert der Computer ZX Spectrum Programme im Speicher und auf Kassetten. Zur Sicherheit habe ich die Schlüsselwörter mit der Tabelleüberprüft. Das Ergebnis war jedoch offenbar negativ.

Ich habe auch die Schlüsselwörter von BASIC der damals beliebten Computer Atari, Commodore 64 und mehreren anderen, für die ich Dokumentationen finden konnte, überprüft, jedoch ohne Erfolg – meine Kenntnisse über die Arten von Retro-Computern waren nicht so umfassend.

Dann beschloss ich, die Liste, und mein Blick fiel auf den Herstellernamen Radio Shack und den Computer TRS-80. Genau diese Namen standen auf den Etiketten der Kassetten, die auf meinem Tisch lagen! Ich kannte diese Namen zuvor nicht und war mit dem Computer TRS-80 nicht vertraut, daher dachte ich, dass Radio Shack ein Hersteller von Audiokassetten wie BASF, Sony oder TDK sei, und TRS-80 die Wiedergabedauer darstellt. Warum nicht?

Computer Tandy/Radio Shack TRS-80

Es ist sehr wahrscheinlich, dass die audiophile Aufnahme, die ich zu Beginn des Artikels als Beispiel angegeben habe, auf einem solchen Computer gemacht wurde:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Es stellte sich heraus, dass dieser Computer und seine Varianten (Model I/Model III/Model IV usw.) zu seiner Zeit sehr beliebt waren (natürlich nicht in Russland). Bemerkenswert ist, dass der Prozessor, der in ihnen verwendet wurde - ebenfalls Z80. Über diesen Computer kann man im Internet viel Informationen finden. In den 80er Jahren verbreitete sich das Wissen über den Computer in Magazinen. Momentan gibt es mehrere Emulatoren des Computers für verschiedene Plattformen.

Ich habe den Emulator trs80gp geladen und hatte zum ersten Mal die Möglichkeit zu sehen, wie dieser Computer funktionierte. Natürlich unterstützte der Computer keine Farbausgabe, die Bildschirmauflösung betrug nur 128x48 Punkte, aber es gab viele Erweiterungen und Modifikationen, die die Bildschirmauflösung erhöhen konnten. Es gab auch viele Varianten von Betriebssystemen für diesen Computer und verschiedene Implementierungen der BASIC-Sprache (die, im Gegensatz zum ZX Spectrum, in einigen Modellen nicht einmal in den ROM eingebrannt war und jede Variante von Diskette geladen werden konnte, ebenso wie das Betriebssystem selbst)

Ich fand auch Dienstprogramm eine Möglichkeit, Audioaufnahmen in das CAS-Format zu konvertieren, welches von Emulatoren unterstützt wird, jedoch konnte ich aus irgendwelchen Gründen meine Aufnahmen von den Kassetten damit nicht lesen.

Nachdem ich das CAS-Dateiformat verstanden hatte (das sich als einfache bitweise Kopie der Daten vom Band erwies, die ich bereits hatte, abgesehen von der Kopfzeile mit dem Synchronisationsbyte), habe ich einige Änderungen an meinem Programm vorgenommen und konnte eine funktionierende CAS-Datei erzeugen, die im Emulator (TRS-80 Model III) funktionierte:

Wie ich Daten im unbekannten Format von Magnetbändern wiederhergestellt habe

Die letzte Version des Dienstprogramms zur Konvertierung mit automatischer Erkennung des ersten Impulses und dem Abstand zwischen den Referenzimpulsen habe ich als GEM-Paket gestaltet, der Quellcode ist verfügbar auf Github.

Fazit

Die zurückgelegte Strecke stellte sich als eine spannende Reise in die Vergangenheit heraus, und ich bin froh, dass ich schließlich die Lösung gefunden habe. Darüber hinaus habe ich:

  • das Datenformat im ZX Spectrum verstanden und die im ROM eingebauten Unterprogramme zum Speichern/Lesen von Daten von Audiokassetten untersucht
  • Ich habe mich mit dem Computer TRS-80 und seinen Varianten vertraut gemacht, das Betriebssystem studiert, Programmbeispiele angesehen und hatte sogar die Möglichkeit, mit Maschinencodes zu debuggen (schließlich sind mir alle Mnemonics des Z80 gut bekannt).
  • Ich habe ein voll funktionsfähiges Tool zum Konvertieren von Audiodateien in das CAS-Format geschrieben, das Daten lesen kann, die von dem "offiziellen" Tool nicht erkannt werden.

Quelle: habr.com

60GB SSD 8Gb DDR4