Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

prapovijest

Budući da sam ljubitelj retro hardvera, jednom sam kupio ZX Spectrum+ od prodavača u Velikoj Britaniji. Uz samo računalo sam dobio nekoliko audio kazeta s igrama (u originalnom pakiranju s uputama), kao i programe snimljene na kazetama bez posebnih oznaka. Iznenađujuće, podaci sa 40 godina starih kazeta bili su dobro čitljivi i s njih sam mogao skinuti gotovo sve igrice i programe.

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Međutim, na nekim sam kazetama pronašao snimke za koje očito nije ZX Spectrum računalo. Zvučale su potpuno drugačije i za razliku od snimki sa spomenutog računala nisu počinjale s kratkim BASIC bootloaderom, koji je inače prisutan na snimkama svih programa i igrica.

Neko vrijeme me to proganjalo - stvarno sam želio saznati što se krije u njima. Kad biste mogli pročitati audio signal kao niz bajtova, mogli biste tražiti znakove ili bilo što što ukazuje na porijeklo signala. Svojevrsna retroarheologija.

Sad kad sam prošao do kraja i pogledao etikete samih kazeta, nasmiješim se jer

odgovor mi je cijelo vrijeme bio pred očima
Na naljepnici lijeve kazete nalazi se naziv računala TRS-80, a odmah ispod imena proizvođača: “Manufactured by Radio Shack in USA”

(Ako želite zadržati intrigu do kraja, nemojte ići ispod spojlera)

Usporedba audio signala

Prije svega, digitalizirajmo audio zapise. Možete poslušati kako to zvuči:


I kao i obično zvuči snimka sa ZX Spectrum računala:


U oba slučaja na početku snimke postoji tzv pilot ton - zvuk iste frekvencije (u prvoj snimci je vrlo kratak <1 sekunde, ali se razlikuje). Pilot ton signalizira računalu da se pripremi za primanje podataka. Svako računalo u pravilu prepoznaje samo svoj "vlastiti" pilot ton prema obliku signala i njegovoj frekvenciji.

Potrebno je nešto reći i o samom obliku signala. Na primjer, na ZX Spectrumu njegov oblik je pravokutan:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Kada se detektira pilot ton, ZX Spectrum prikazuje naizmjenične crvene i plave trake na rubu zaslona kako bi označio da je signal prepoznat. Pilot ton završava sinkro puls, što daje signal računalu da počne primati podatke. Karakterizira ga kraće trajanje (u usporedbi s pilot tonom i naknadnim podacima) (vidi sliku)

Nakon što se sinkronizacijski impuls primi, računalo bilježi svaki porast/pad signala, mjereći njegovo trajanje. Ako je trajanje manje od određenog ograničenja, bit 1 se upisuje u memoriju, inače 0. Bitovi se skupljaju u bajtove i proces se ponavlja dok se ne primi N bajtova. Broj N obično se uzima iz zaglavlja preuzete datoteke. Slijed učitavanja je sljedeći:

  1. pilot ton
  2. zaglavlje (fiksne duljine), sadrži veličinu preuzetih podataka (N), naziv datoteke i vrstu
  3. pilot ton
  4. sami podaci

Kako bi bili sigurni da su podaci ispravno učitani, ZX Spectrum čita tzv paritetni bajt (paritetni bajt), koji se izračunava prilikom spremanja datoteke XOR-om svih bajtova zapisanih podataka. Prilikom čitanja datoteke, računalo izračunava paritetni bajt iz primljenih podataka i, ako se rezultat razlikuje od spremljenog, prikazuje poruku o pogrešci “R Tape loading error”. Strogo govoreći, računalo može izdati ovu poruku ranije ako prilikom očitavanja ne može prepoznati puls (propušten ili njegovo trajanje ne odgovara određenim granicama)

Dakle, pogledajmo sada kako izgleda nepoznati signal:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Ovo je pilot ton. Oblik signala je bitno drugačiji, ali je jasno da se signal sastoji od ponavljajućih kratkih impulsa određene frekvencije. Na frekvenciji uzorkovanja od 44100 Hz, udaljenost između "vrhova" je približno 48 uzoraka (što odgovara frekvenciji od ~918 Hz). Zapamtimo ovu brojku.

Pogledajmo sada fragment podataka:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Ako izmjerimo udaljenost između pojedinačnih impulsa, ispada da je udaljenost između "dugih" impulsa još uvijek ~48 uzoraka, a između kratkih - ~24. Gledajući malo unaprijed, reći ću da se na kraju pokazalo da "referentni" impulsi s frekvencijom od 918 Hz slijede kontinuirano, od početka do kraja datoteke. Može se pretpostaviti da prilikom prijenosa podataka, ako se naiđe na dodatni impuls između referentnih impulsa, smatramo ga bitom 1, inače 0.

Što je sa sinkronizirajućim pulsom? Pogledajmo početak podataka:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Pilot ton završava i podaci počinju odmah. Nešto kasnije, nakon analize nekoliko različitih audio zapisa, uspjeli smo otkriti da je prvi bajt podataka uvijek isti (10100101b, A5h). Računalo može početi čitati podatke nakon što ih primi.

Također možete obratiti pozornost na pomak prvog referentnog impulsa neposredno nakon zadnjeg 1. u sinkro bajtu. To je otkriveno mnogo kasnije u procesu razvoja programa za prepoznavanje podataka, kada se podaci na početku datoteke nisu mogli stabilno čitati.

Pokušajmo sada opisati algoritam koji će obraditi audio datoteku i učitati podatke.

Učitavanje podataka

Prvo, pogledajmo nekoliko pretpostavki kako bi algoritam bio jednostavan:

  1. Razmotrit ćemo samo datoteke u WAV formatu;
  2. Audio datoteka mora započeti pilot tonom i ne smije sadržavati tišinu na početku
  3. Izvorna datoteka mora imati brzinu uzorkovanja od 44100 Hz. U ovom slučaju, udaljenost između referentnih impulsa 48 uzoraka je već određena i ne trebamo je izračunavati programski;
  4. Format uzorka može biti bilo koji (8/16 bita/pokretni zarez) - budući da ga prilikom čitanja možemo pretvoriti u željeni;
  5. Pretpostavljamo da je izvorna datoteka normalizirana po amplitudi, što bi trebalo stabilizirati rezultat;

Algoritam čitanja bit će sljedeći:

  1. Datoteku čitamo u memoriju, istovremeno pretvarajući format uzorka u 8 bita;
  2. Odredite položaj prvog impulsa u audio podacima. Da biste to učinili, morate izračunati broj uzorka s maksimalnom amplitudom. Radi jednostavnosti, izračunat ćemo ga jednom ručno. Spremimo ga u varijablu prev_pos;
  3. Dodajte 48 poziciji zadnjeg impulsa (pos := prev_pos + 48)
  4. Budući da povećanje pozicije za 48 ne jamči da ćemo doći do pozicije sljedećeg referentnog impulsa (defekti vrpce, nestabilan rad mehanizma pogona trake itd.), potrebno je podesiti poziciju impulsa poz. Da biste to učinili, uzmite mali dio podataka (pos-8;pos+8) i na njemu pronađite maksimalnu vrijednost amplitude. Položaj koji odgovara maksimumu bit će pohranjen u poz. Ovdje je 8 = 48/6 eksperimentalno dobivena konstanta, koja jamči da ćemo odrediti točan maksimum i neće utjecati na druge impulse koji bi mogli biti u blizini. U vrlo lošim slučajevima, kada je udaljenost između impulsa puno manja ili veća od 48, možete implementirati prisilno traženje pulsa, ali u okviru članka neću to opisivati ​​u algoritmu;
  5. U prethodnom koraku također bi bilo potrebno provjeriti je li referentni puls uopće pronađen. Odnosno, ako jednostavno tražite maksimum, to ne jamči da je impuls prisutan u ovom segmentu. U mojoj posljednjoj implementaciji programa za čitanje, provjeravam razliku između maksimalne i minimalne vrijednosti amplitude na segmentu, i ako prelazi određenu granicu, računam prisutnost impulsa. Također se postavlja pitanje što učiniti ako referentni puls nije pronađen. Postoje 2 opcije: ili su podaci završili i slijedi tišina ili se to treba smatrati pogreškom u čitanju. Međutim, ovo ćemo izostaviti kako bismo pojednostavili algoritam;
  6. U sljedećem koraku trebamo utvrditi prisutnost podatkovnog impulsa (bit 0 ili 1), za to uzimamo sredinu segmenta (prev_pos;pos) Middle_pos jednaku Middle_pos := (prev_pos+pos)/2 i u nekom susjedstvu srednjeg_posa na segmentu (srednji_pos-8; srednji_pos +8) izračunajmo maksimalnu i minimalnu amplitudu. Ako je razlika između njih veća od 10, u rezultat upisujemo bit 1, inače 0. 10 je konstanta dobivena eksperimentalno;
  7. Spremi trenutni položaj u prev_pos (prev_pos := pos)
  8. Ponavljajte počevši od koraka 3 dok ne pročitamo cijelu datoteku;
  9. Rezultirajući niz bitova mora se spremiti kao skup bajtova. Budući da prilikom čitanja nismo uzeli u obzir bajt sinkronizacije, broj bitova ne smije biti višekratnik broja 8, a potreban pomak bita je također nepoznat. U prvoj implementaciji algoritma nisam znao za postojanje sinkronizirajućeg bajta i stoga sam jednostavno spremio 8 datoteka s različitim brojem offset bitova. Jedan od njih sadržavao je točne podatke. U konačnom algoritmu jednostavno uklanjam sve bitove do A5h, što mi omogućuje da odmah dobijem ispravnu izlaznu datoteku

Algoritam u Rubyju, za zainteresirane
Odabrao sam Ruby kao jezik za pisanje programa, jer... Većinu vremena programiram na njemu. Opcija nije visokih performansi, ali zadatak da se brzina čitanja što je moguće brže ne isplati.

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

Rezultirati

Isprobavši nekoliko varijanti algoritma i konstanti, imao sam sreće da dobijem nešto izuzetno zanimljivo:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Dakle, sudeći po znakovnim nizovima, imamo program za iscrtavanje grafova. Međutim, u tekstu programa nema ključnih riječi. Sve su ključne riječi kodirane kao bajtovi (svaka vrijednost > 80h). Sada moramo saznati koje je računalo iz 80-ih moglo spremati programe u ovom formatu.

Zapravo, vrlo je sličan BASIC programu. Računalo ZX Spectrum pohranjuje programe u približno istom formatu u memoriju i sprema programe na vrpcu. Za svaki slučaj, provjerio sam ključne riječi stol. Međutim, rezultat je očito bio negativan.

Provjeravao sam i BASIC ključne riječi popularnog Atarija, Commodorea 64 i još nekoliko tadašnjih računala, za koje sam uspio pronaći dokumentaciju, ali bezuspješno - pokazalo se da moje poznavanje tipova retro računala nije toliko široko.

Onda sam odlučio otići popis, a onda mi je pogled pao na ime proizvođača Radio Shack i računala TRS-80. Ovo su imena koja su bila ispisana na naljepnicama kaseta koje su ležale na mom stolu! Prije nisam poznavao ova imena i nisam bio upoznat s računalom TRS-80, pa mi se činilo da je Radio Shack proizvođač audio kazeta kao što su BASF, Sony ili TDK, a TRS-80 je vrijeme reprodukcije. Zašto ne?

Računalo Tandy/Radio Shack TRS-80

Vrlo je vjerojatno da je dotična audio snimka, koju sam naveo kao primjer na početku članka, nastala na ovakvom računalu:

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Ispostavilo se da su ovo računalo i njegove varijante (Model I/Model III/Model IV, itd.) jedno vrijeme bili vrlo popularni (naravno, ne u Rusiji). Važno je napomenuti da je procesor koji su koristili također Z80. Za ovo računalo možete pronaći na internetu puno informacija. U 80-ima su računalne informacije distribuirane u časopisi. Trenutno ih je nekoliko emulatore računala za različite platforme.

Skinuo sam emulator trs80gp i po prvi put sam mogao vidjeti kako ovo računalo radi. Naravno, računalo nije podržavalo ispis u boji; razlučivost zaslona bila je samo 128x48 piksela, ali bilo je mnogo proširenja i izmjena koje su mogle povećati razlučivost zaslona. Bilo je i mnogo opcija za operativne sustave za ovo računalo i mogućnosti za implementaciju BASIC jezika (koji za razliku od ZX Spectruma kod nekih modela nije čak ni bio “flašovan” u ROM već se bilo koja opcija mogla učitati s diskete, baš kao sam OS)

Također sam pronašao korisnost za pretvaranje audio zapisa u CAS format, koji podržavaju emulatori, ali iz nekog razloga pomoću njih nije bilo moguće čitati snimke s mojih kazeta.

Nakon što sam shvatio CAS format datoteke (za koji se pokazalo da je samo bit-po-bit kopija podataka s vrpce koju sam već imao pri ruci, osim zaglavlja s prisutnošću bajta za sinkronizaciju), napravio sam nekoliko promjena u mom programu i uspio sam ispisati radnu CAS datoteku koja je radila u emulatoru (TRS-80 Model III):

Kako sam oporavio podatke u nepoznatom formatu s magnetske trake

Dizajnirao sam najnoviju verziju uslužnog programa za pretvorbu s automatskim određivanjem prvog impulsa i udaljenosti između referentnih impulsa kao GEM paket, izvorni kod je dostupan na Github.

Zaključak

Put kojim smo prošli pokazao se kao fascinantno putovanje u prošlost i drago mi je da sam na kraju pronašao odgovor. Između ostalog, ja:

  • Shvatio sam format za spremanje podataka u ZX Spectrum i proučavao ugrađene ROM rutine za spremanje/čitanje podataka s audio kazeta
  • Upoznao sam se s računalom TRS-80 i njegovim varijantama, proučavao operativni sustav, pogledao ogledne programe i čak imao priliku raditi debugging u strojnim kodovima (uostalom, sve mnemotehnike Z80 su mi poznate)
  • Napisao je punopravni uslužni program za pretvaranje audio zapisa u CAS format, koji može čitati podatke koje "službeni" uslužni program ne prepoznaje

Izvor: www.habr.com

Dodajte komentar