Kako sem z magnetnega traku obnovil podatke v neznani obliki

prazgodovina

Ker sem ljubitelj retro strojne opreme, sem nekoč kupil ZX Spectrum+ pri prodajalcu v Veliki Britaniji. Zraven samega računalnika sem dobil več avdio kaset z igricami (v originalni embalaži z navodili), ter programe posnete na kasete brez posebnih oznak. Presenetljivo je bilo, da so bili podatki s 40 let starih kaset dobro berljivi in ​​sem lahko z njih prenesel skoraj vse igre in programe.

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Sem pa na nekaterih kasetah našel posnetke, ki očitno niso narejeni z računalnikom ZX Spectrum. Zveneli so povsem drugače in se za razliko od posnetkov z omenjenega računalnika niso začeli s kratkim BASIC bootloaderjem, ki je običajno prisoten v posnetkih vseh programov in iger.

Nekaj ​​časa me je to preganjalo – res sem si želela odkriti, kaj se skriva v njih. Če bi lahko zvočni signal prebrali kot zaporedje bajtov, bi lahko iskali znake ali karkoli, kar kaže na izvor signala. Nekakšna retroarheologija.

Zdaj, ko sem šel do konca in pogledal nalepke samih kaset, se nasmejim, ker

odgovor mi je bil ves čas pred očmi
Na nalepki leve kasete je ime računalnika TRS-80, tik pod imenom proizvajalca pa: “Manufactured by Radio Shack in USA”

(Če želite ohraniti spletko do konca, ne pojdite pod spojler)

Primerjava zvočnih signalov

Najprej digitalizirajmo zvočne posnetke. Lahko poslušate, kako zveni:


In kot običajno se sliši posnetek z računalnika ZX Spectrum:


V obeh primerih je na začetku posnetka t.i pilotski ton - zvok enake frekvence (na prvem posnetku je zelo kratek <1 sekunde, a razločen). Pilotni ton signalizira računalniku, da se pripravi na sprejem podatkov. Praviloma vsak računalnik po obliki signala in njegovi frekvenci prepozna le »lastni« pilotni ton.

Treba je povedati nekaj o sami obliki signala. Na primer, na ZX Spectrum je njegova oblika pravokotna:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Ko je zaznan pilotni ton, ZX Spectrum prikaže izmenično rdeče in modre črte na robu zaslona, ​​kar pomeni, da je bil signal prepoznan. Pilotni ton se konča sinhroni impulz, ki računalniku sporoči, da začne prejemati podatke. Zanj je značilno krajše trajanje (v primerjavi s pilotnim tonom in kasnejšimi podatki) (glej sliko)

Po prejemu sinhronizacijskega impulza računalnik zabeleži vsak dvig/padec signala in izmeri njegovo trajanje. Če je trajanje manjše od določene meje, se v pomnilnik zapiše bit 1, drugače pa 0. Biti se zberejo v bajte in postopek se ponavlja, dokler ni prejetih N bajtov. Številka N je običajno vzeta iz glave prenesene datoteke. Zaporedje nalaganja je naslednje:

  1. pilotski ton
  2. glava (fiksna dolžina), vsebuje velikost prenesenih podatkov (N), ime in vrsto datoteke
  3. pilotski ton
  4. podatki sami

Za zagotovitev, da so podatki pravilno naloženi, ZX Spectrum bere t.i paritetni bajt (parnostni bajt), ki se izračuna pri shranjevanju datoteke z XOR-jem vseh bajtov zapisanih podatkov. Pri branju datoteke računalnik izračuna paritetni bajt iz prejetih podatkov in, če se rezultat razlikuje od shranjenega, prikaže sporočilo o napaki “R Tape loading error”. Strogo gledano lahko računalnik izda to sporočilo prej, če pri branju ne more prepoznati utripa (zgrešen ali njegovo trajanje ne ustreza določenim mejam)

Torej, poglejmo zdaj, kako izgleda neznan signal:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

To je pilotski ton. Oblika signala je bistveno drugačna, vendar je jasno, da je signal sestavljen iz ponavljajočih se kratkih impulzov določene frekvence. Pri frekvenci vzorčenja 44100 Hz je razdalja med "vrhovi" približno 48 vzorcev (kar ustreza frekvenci ~918 Hz). Zapomnimo si to številko.

Poglejmo zdaj fragment podatkov:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Če izmerimo razdaljo med posameznimi impulzi, se izkaže, da je razdalja med "dolgimi" impulzi še vedno ~ 48 vzorcev in med kratkimi - ~ 24. Če pogledam malo naprej, bom rekel, da se je na koncu izkazalo, da "referenčni" impulzi s frekvenco 918 Hz sledijo neprekinjeno, od začetka do konca datoteke. Predpostavimo lahko, da pri prenosu podatkov, če med referenčnimi impulzi naletimo na dodaten impulz, to obravnavamo kot bit 1, sicer kot 0.

Kaj pa sinhronizacijski impulz? Poglejmo na začetku podatkov:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Pilotni ton se konča in podatki se takoj začnejo. Malo kasneje smo po analizi več različnih zvočnih posnetkov ugotovili, da je prvi bajt podatkov vedno enak (10100101b, A5h). Računalnik lahko začne brati podatke, potem ko jih prejme.

Pozorni ste lahko tudi na premik prvega referenčnega impulza takoj za zadnjim 1. v sinhronizacijskem bajtu. Odkrili so ga veliko kasneje v procesu razvoja programa za prepoznavanje podatkov, ko podatkov na začetku datoteke ni bilo mogoče stabilno brati.

Zdaj pa poskusimo opisati algoritem, ki bo obdelal zvočno datoteko in naložil podatke.

Nalaganje podatkov

Najprej si oglejmo nekaj predpostavk, da bo algoritem preprost:

  1. Upoštevali bomo samo datoteke v formatu WAV;
  2. Zvočna datoteka se mora začeti s pilotnim tonom in ne sme vsebovati tišine na začetku
  3. Izvorna datoteka mora imeti frekvenco vzorčenja 44100 Hz. V tem primeru je razdalja med referenčnimi impulzi 48 vzorcev že določena in nam je ni treba izračunati programsko;
  4. Format vzorca je lahko poljuben (8/16 bitov/plavajoča vejica) - saj ga pri branju lahko pretvorimo v želenega;
  5. Predvidevamo, da je izvorna datoteka normalizirana po amplitudi, kar naj bi stabiliziralo rezultat;

Algoritem branja bo naslednji:

  1. Datoteko preberemo v pomnilnik, hkrati pretvorimo format vzorca v 8 bitov;
  2. Določite položaj prvega impulza v zvočnih podatkih. Če želite to narediti, morate izračunati število vzorca z največjo amplitudo. Zaradi enostavnosti ga bomo izračunali enkrat ročno. Shranimo ga v spremenljivko prev_pos;
  3. Dodajte 48 položaju zadnjega impulza (pos := prev_pos + 48)
  4. Ker povečanje položaja za 48 ne zagotavlja, da bomo prišli do položaja naslednjega referenčnega impulza (okvare traku, nestabilno delovanje tračnega pogonskega mehanizma itd.), moramo prilagoditi položaj impulza poz. Če želite to narediti, vzemite majhen delček podatkov (pos-8;pos+8) in na njem poiščite največjo vrednost amplitude. Položaj, ki ustreza maksimumu, bo shranjen v poz. Tukaj je 8 = 48/6 eksperimentalno pridobljena konstanta, ki zagotavlja, da bomo določili pravilen maksimum in ne bomo vplivali na druge impulze, ki so lahko v bližini. V zelo slabih primerih, ko je razdalja med impulzi veliko manjša ali večja od 48, lahko izvedete prisilno iskanje impulza, vendar v okviru članka tega ne bom opisoval v algoritmu;
  5. Pri prejšnjem koraku bi bilo treba tudi preveriti, ali je bil referenčni impulz sploh najden. Se pravi, če preprosto iščete maksimum, to ne zagotavlja, da je impulz prisoten v tem segmentu. V svoji zadnji izvedbi programa za branje preverim razliko med največjo in najmanjšo vrednostjo amplitude na segmentu in če ta preseže določeno mejo, štejem prisotnost impulza. Vprašanje je tudi, kaj storiti, če referenčnega impulza ne najdemo. Obstajata dve možnosti: ali so se podatki končali in sledi tišina ali pa je treba to obravnavati kot napako pri branju. Vendar bomo zaradi poenostavitve algoritma to izpustili;
  6. V naslednjem koraku moramo ugotoviti prisotnost podatkovnega impulza (bit 0 ali 1), za to vzamemo sredino segmenta (prev_pos;pos) middle_pos enako Middle_pos := (prev_pos+pos)/2 in v neki okolici srednjega_posa na segmentu (srednji_pos-8; srednji_pos +8) izračunajmo največjo in najmanjšo amplitudo. Če je razlika med njima večja od 10, v rezultat vpišemo bit 1, sicer 0. 10 je eksperimentalno pridobljena konstanta;
  7. Shrani trenutni položaj v prev_pos (prev_pos := pos)
  8. Ponavljajte od koraka 3, dokler ne preberemo celotne datoteke;
  9. Nastala bitna matrika mora biti shranjena kot niz bajtov. Ker pri branju nismo upoštevali sinhronizacijskega bajta, število bitov ne sme biti večkratnik 8, prav tako ni znan zahtevani bitni odmik. Pri prvi izvedbi algoritma nisem vedel za obstoj sinhronizacijskega bajta in sem zato preprosto shranil 8 datotek z različnim številom offset bitov. Eden od njih je vseboval pravilne podatke. V končnem algoritmu preprosto odstranim vse bite do A5h, kar mi omogoča, da takoj dobim pravilno izhodno datoteko

Algoritem v Rubyju, za tiste, ki jih zanima
Za jezik pisanja programa sem izbral Ruby, ker... Večino časa programiram na njem. Možnost ni visoko zmogljiva, vendar naloga čim hitrejšega branja ni vredna.

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

Rezultat

Ko sem preizkusil več različic algoritma in konstant, sem imel srečo, da sem dobil nekaj izjemno zanimivega:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Torej, sodeč po znakovnih nizih, imamo program za risanje grafov. Vendar pa v besedilu programa ni ključnih besed. Vse ključne besede so kodirane kot bajti (vsaka vrednost > 80h). Zdaj moramo ugotoviti, kateri računalnik iz 80-ih bi lahko shranil programe v tem formatu.

Pravzaprav je zelo podoben programu BASIC. Računalnik ZX Spectrum shranjuje programe v približno enakem formatu v pomnilnik in jih shranjuje na trak. Za vsak slučaj sem preveril ključne besede tabela. Vendar je bil rezultat očitno negativen.

Preveril sem tudi BASIC ključne besede priljubljenih Atarija, Commodoreja 64 in več drugih računalnikov tistega časa, za katere sem uspel najti dokumentacijo, vendar brez uspeha - izkazalo se je, da moje poznavanje vrst retro računalnikov ni tako široko.

Potem sem se odločil, da grem seznam, nato pa mi je pogled padel na ime proizvajalca Radio Shack in računalnik TRS-80. To so imena, ki so bila zapisana na nalepkah kaset, ki so ležale na moji mizi! Prej nisem poznal teh imen in nisem poznal računalnika TRS-80, zato se mi je zdelo, da je Radio Shack proizvajalec avdio kaset, kot so BASF, Sony ali TDK, TRS-80 pa je čas predvajanja. Zakaj ne?

Računalniški Tandy/Radio Shack TRS-80

Zelo verjetno je, da je bil zadevni zvočni posnetek, ki sem ga navedel kot primer na začetku članka, narejen na računalniku, kot je ta:

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Izkazalo se je, da so bili ta računalnik in njegove različice (Model I/Model III/Model IV itd.) nekoč zelo priljubljeni (seveda ne v Rusiji). Omeniti velja, da je bil uporabljen tudi procesor Z80. Za ta računalnik lahko najdete na internetu veliko informacij. V 80. letih so se računalniške informacije širile v revije. Trenutno jih je več emulatorji računalniki za različne platforme.

Prenesel sem emulator trs80gp in prvič sem lahko videl, kako ta računalnik deluje. Seveda računalnik ni podpiral barvnega izpisa; ločljivost zaslona je bila le 128 x 48 slikovnih pik, vendar je bilo veliko razširitev in sprememb, ki bi lahko povečale ločljivost zaslona. Veliko je bilo tudi možnosti operacijskih sistemov za ta računalnik in možnosti za implementacijo jezika BASIC (ki za razliko od ZX Spectruma pri nekaterih modelih ni bil niti "flašnat" v ROM in je bilo možno poljubno možnost naložiti z diskete, tako kot npr. sam OS)

tudi našel sem uporabnost za pretvorbo zvočnih posnetkov v format CAS, ki ga podpirajo emulatorji, vendar iz neznanega razloga z njimi ni bilo mogoče brati posnetkov z mojih kaset.

Ko sem ugotovil format datoteke CAS (za katerega se je izkazalo, da je le pobitna kopija podatkov s traku, ki sem ga že imel pri roki, razen glave s prisotnostjo sinhronizacijskega bajta), sem naredil nekaj sprememb v mojem programu in uspel sem izpisati delujočo datoteko CAS, ki je delovala v emulatorju (TRS-80 Model III):

Kako sem z magnetnega traku obnovil podatke v neznani obliki

Zasnoval sem najnovejšo različico pripomočka za pretvorbo s samodejnim določanjem prvega impulza in razdalje med referenčnimi impulzi kot paket GEM, izvorna koda je na voljo na GitHub.

Zaključek

Pot, ki sva jo prehodila, se je izkazala za fascinantno potovanje v preteklost in vesela sem, da sem na koncu našla odgovor. Med drugim sem:

  • Ugotovil sem format za shranjevanje podatkov v ZX Spectrum in preučil vgrajene ROM rutine za shranjevanje/branje podatkov z avdio kaset
  • Seznanil sem se z računalnikom TRS-80 in njegovimi različicami, preučil operacijski sistem, si ogledal vzorčne programe in imel celo možnost odpravljanja napak v strojnih kodah (navsezadnje so mi vse mnemonike Z80 znane)
  • Napisal polnopravni pripomoček za pretvorbo zvočnih posnetkov v format CAS, ki lahko bere podatke, ki jih "uradni" pripomoček ne prepozna

Vir: www.habr.com

Dodaj komentar