Kiel mi reakiris datumojn en nekonata formato de magneta bendo

antaŭhistorio

Estante ŝatanto de retroa aparataro, mi iam aĉetis ZX Spectrum+ de brita vendisto. Kune kun la komputilo mem, mi ricevis plurajn sonkasedojn enhavantajn ludojn (en la originala pakaĵo kun instrukcioj), kaj ankaŭ programojn registritajn sur kasedoj sen iuj specialaj markoj. Surprize, la datumoj de la 40-jaraj kasedoj estis facile legeblaj, kaj mi povis ŝargi preskaŭ ĉiujn ludojn kaj programojn de ili.

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Tamen, sur kelkaj kasedoj, mi trovis registraĵojn, kiuj klare ne estis faritaj de komputilo ZX Spectrum. Ili sonis tute malsame kaj, male al la registraĵoj de la jam menciita komputilo, ne komenciĝis per la mallonga BASIC-ŝargilo, kiu kutime ĉeestas en registraĵoj de ĉiuj programoj kaj ludoj.

Tio persekutis min dum kelka tempo — mi vere volis scii, kio kaŝiĝis en ili. Se mi povus legi la sonsignalon kiel sinsekvon de bajtoj, mi povus serĉi simbolojn aŭ ion ajn, kio indikus la originon de la signalo. Ia speco de retroarkeologio.

Nun, kiam mi jam iris la tutan vojon kaj rigardis la etikedojn de la kasedoj mem, mi ridetis ĉar

la respondo estis rekte antaŭ miaj okuloj la tutan tempon
La etikedo sur la maldekstra kasedo havas la nomon de la komputilo TRS-80, kaj ĝuste sube la nomon de la fabrikanto: "Fabrikita de Radio Shack en Usono"

(Se vi volas daŭrigi la suspenson ĝis la fino, ne alklaku la spoiler-on)

Komparo de sonsignaloj

Unue, ni ciferecigu la sonregistraĵojn. Vi povas aŭskulti kiel ĝi sonas:


Kaj kiel kutime, sonas registrado de komputilo de ZX Spectrum:


En ambaŭ kazoj, komence de la registrado estas tiel nomata pilota tono — unu-frekvenca sono (en la unua registrado, ĝi estas tre mallonga, <1 sekundo, sed tamen distingebla). La pilottono signalas al la komputilo prepariĝi por ricevi datumojn. Tipe, ĉiu komputilo rekonas nur sian propran pilottonon bazitan sur sia signalformo kaj frekvenco.

Indas mencii la signalformon mem. Ekzemple, ĉe la ZX Spectrum, ĝia formo estas rektangula:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Kiam pilottono estas detektita, la ZX Spectrum montras alternajn ruĝajn kaj bluajn striojn ĉe la rando de la ekrano, indikante ke la signalo estis rekonita. La pilottono finiĝas. sinkroniga pulso, kiu signalas al la komputilo komenci ricevi datumojn. Ĝin karakterizas pli mallonga daŭro (kompare kun la pilottono kaj postaj datumoj) (vidu figuron).

Post kiam la sinkroniga pulso estas ricevita, la komputilo registras ĉiun altiĝon/malaltiĝon de la signalo, mezurante ĝian daŭron. Se la daŭro estas malpli ol certa limo, 1-bito estas skribita en la memoron; alie, 0-bito estas skribita. La bitoj estas kolektitaj en bajtojn, kaj la procezo ripetiĝas ĝis N bajtoj estas ricevitaj. La nombro N estas tipe prenita el la kaplinio de la ŝarĝata dosiero. La ŝarĝa sekvenco estas jena:

  1. pilota tono
  2. kaplinio (fiksa longo), enhavas la grandecon de la elŝutotaj datumoj (N), la nomon kaj tipon de la dosiero
  3. pilota tono
  4. la datumoj mem

Por certigi, ke la datumoj estas ŝarĝitaj ĝuste, la ZX Spectrum legas la tiel nomatan egaleco bajto (parecbajto), kiu estas kalkulata dum konservado de dosiero per XOR-a ago de ĉiuj bajtoj de la skribitaj datumoj. Dum legado de dosiero, la komputilo kalkulas la parecbajton el la ricevitaj datumoj kaj, se la rezulto diferencas de la konservita, montras la erarmesaĝon "R Tape loading error" (Eraro dum ŝarĝado de R Tape). Strikte parolante, la komputilo povas montri ĉi tiun mesaĝon pli frue se ĝi ne povas rekoni pulson dum legado (ĝi mankas aŭ ĝia daŭro ne plenumas certajn limojn).

Do, ni nun vidu kiel aspektas nekonata signalo:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Jen pilottono. La ondformo estas signife malsama, sed estas klare, ke la signalo konsistas el ripetado de mallongaj pulsoj de specifa frekvenco. Ĉe specimenfrekvenco de 44 100 Hz, la distanco inter la "pintoj" estas proksimume 48 specimenoj (korespondante al frekvenco de ~918 Hz). Ni memoru ĉi tiun ciferon.

Ni nun rigardu la datenfragmenton:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Se ni mezuras la distancon inter individuaj pulsoj, ni trovas, ke la distanco inter "longaj" pulsoj estas ankoraŭ ~48 specimenoj, dum inter mallongaj ĝi estas ~24. Iom antaŭenirante, mi diros, ke montriĝis, ke la "referencaj" pulsoj kun frekvenco de 918 Hz sekvas kontinue, de la komenco ĝis la fino de la dosiero. Oni povas supozi, ke dum datentransdono, se plia pulso okazas inter la referencaj pulsoj, ni kalkulas ĝin kiel 1-biton, alie kiel 0.

Kio pri la sinkroniga pulso? Ni rigardu la komencon de la datumoj:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

La pilottono finiĝas kaj la datumoj tuj komenciĝas. Iom poste, post analizo de pluraj malsamaj sonregistraĵoj, oni malkovris, ke la unua bajto de datumoj ĉiam estas la sama (10100101b, A5h). Eble la komputilo komencas legi la datumojn post ricevo de ili.

Vi ankaŭ povas rimarki la ŝoviĝon en la unua referenca pulso tuj post la lasta 1 en la sinkroniga bajto. Ĉi tio estis malkovrita multe pli poste dum la disvolviĝo de la datenrekona programo, kiam la datumoj komence de la dosiero ne povis esti fidinde legitaj.

Nun ni provu priskribi la algoritmon, kiu prilaboros la sondosieron kaj ŝarĝos la datumojn.

Ŝarĝante Datumojn

Ni unue konsideru kelkajn supozojn por teni la algoritmon simpla:

  1. Ni konsideros dosierojn nur en WAV-formato;
  2. La sondosiero devas komenciĝi per pilottono kaj ne rajtas enhavi silenton komence.
  3. La fontdosiero devas havi specimenan frekvencon de 44100 Hz. En ĉi tiu kazo, la distanco inter referencaj pulsoj de 48 specimenoj jam estas determinita kaj ni ne bezonas kalkuli ĝin programece;
  4. La ekzempla formato povas esti ajna (8/16 bitoj/glitkomo) - ĉar legante ni povas konverti ĝin al la bezonata;
  5. Ni supozas, ke la fontdosiero estas amplitude normaligita, kio devus stabiligi la rezulton;

La algoritmo de legado estos jena:

  1. Ni legis la dosieron en la memoron, samtempe konvertante la specimenan formaton al 8 bitoj;
  2. Ni determinas la pozicion de la unua pulso en la sondatumoj. Por fari tion, ni bezonas kalkuli la specimenan nombron kun la maksimuma amplitudo. Por simpleco, ni kalkulos ĝin permane unufoje. Ni konservos ĝin en la variablo prev_pos.
  3. Aldonu 48 al la pozicio de la lasta pulso (poz := prev_pos + 48)
  4. Ĉar pliigi la pozicion je 48 ne garantias, ke ni trafos la sekvan referencan pulson (pro difektoj de la bendo, malstabila funkciado de la bendolegilo, ktp.), ni bezonas ĝustigi la pozicion de la pozicia pulso. Por fari tion, ni prenos malgrandan datumsegmenton (poz-8;poz+8) kaj trovos la maksimuman amplitudvaloron. Ni konservos la pozicion respondantan al la maksimumo en poz. Ĉi tie, 8 = 48/6 estas eksperimente akirita konstanto, kiu garantias, ke ni trovos la ĝustan maksimumon kaj ne influos aliajn pulsojn, kiuj eble estas proksimaj. En tre malbonaj kazoj, kiam la distanco inter pulsoj estas signife malpli aŭ pli granda ol 48, ni povas efektivigi devigan pulsserĉon, sed mi ne priskribos tion en la algoritmo por la celoj de ĉi tiu artikolo.
  5. En la antaŭa paŝo, ankaŭ necesus kontroli ĉu la referenca pulso efektive estis trovita. Tio estas, simpla serĉado de la maksimumo ne garantias, ke pulso ĉeestas en difinita segmento. En mia plej nova efektivigo de la legprogramo, mi kontrolas la diferencon inter la maksimumaj kaj minimumaj amplitudaj valoroj en segmento, kaj se ĝi superas certan limon, mi kalkulas la ĉeeston de pulso. Alia demando estas kion fari se la referenca pulso ne estas trovita. Estas du ebloj: aŭ la datumoj finiĝis kaj estas silento, aŭ tio estu konsiderata kiel lega eraro. Tamen, ni preterlasos tion por simpligi la algoritmon.
  6. La sekva paŝo estas determini la ĉeeston de datenpulso (bito 0 aŭ 1). Por fari tion, ni prenas la mezpunkton de la segmento (antaŭa_pozicio;pozicio) meza_pozicio egala al meza_pozicio := (antaŭa_pozicio+pozicio)/2 kaj kalkulas la maksimumajn kaj minimumajn amplitudojn en iu proksimeco de meza_pozicio sur la segmento (meza_pozicio-8;meza_pozicio+8). Se la diferenco inter ili estas pli granda ol 10, ni skribas biton 1 al la rezulto; alie, ni skribas 0. 10 estas konstanto akirita empirie;
  7. Konservu la nunan pozicion en prev_pos (prev_pos := pos)
  8. Ni ripetas ekde paŝo 3 ĝis ni legis la tutan dosieron;
  9. La rezulta bitmapo devas esti konservita kiel aro da bajtoj. Ĉar ni ne konsideris la sinkronan bajton dum legado, la nombro da bitoj eble ne estas multoblo de 8, kaj la bezonata bita delokigo ankaŭ estas nekonata. En la unua efektivigo de la algoritmo, mi ne sciis pri la sinkrona bajto kaj tial simple konservis ok dosierojn kun malsamaj nombroj da delokigbitoj. Unu el ili enhavis la ĝustajn datumojn. En la fina algoritmo, mi simple forigas ĉiujn bitojn ĝis A5h, kio permesas al mi tuj akiri la ĝustan eligan dosieron.

Algoritmo en Ruby, por tiuj interesataj
Mi elektis Ruby kiel la programlingvon ĉar mi pasigas la plejparton de mia tempo programante per ĝi. Ĝi ne estas alt-efikeca opcio, sed maksimumigi legrapidecon ne estas mia celo.

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

rezulto

Post provado de pluraj variaĵoj de la algoritmo kaj konstantoj, mi havis la bonŝancon trovi ion ekstreme interesan:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Do, juĝante laŭ la signoĉenoj, ni havas programon por desegni grafikaĵojn. Tamen, al la programteksto mankas ŝlosilvortoj. Ĉiuj ŝlosilvortoj estas ĉifritaj kiel bajtoj (ĉiu valoro > 80h). Nun ni devas eltrovi, kiu komputilo el la 80-aj jaroj povis konservi programojn en ĉi tiu formato.

Fakte, ĝi estas tre simila al BASIC-programo. La komputilo ZX Spectrum uzas proksimume la saman formaton por konservi programojn en memoro kaj konservi ilin sur bendo. Por ĉiuokaze, mi kontrolis la ŝlosilvortojn kontraŭ tabloTamen, la rezulto estis evidente negativa.

Mi ankaŭ kontrolis la ŝlosilvortojn de BASIC por la popularaj komputiloj de la tempo, la Atari, Commodore 64, kaj kelkaj aliaj por kiuj mi povis trovi dokumentaron, sed sen sukceso — mia scio pri retroaj komputilaj variaĵoj ne estis tiel vasta.

Tiam mi decidis iri por la listo, kaj tiam mia rigardo falis sur la nomon de la fabrikanto — Radio Shack — kaj la komputilon TRS-80. Ĉi tiuj estis la nomoj skribitaj sur la etikedoj de la kasedoj kuŝantaj sur mia skribotablo! Mi ne aŭdis pri ĉi tiuj nomoj antaŭe, nek mi konis la komputilon TRS-80, do mi supozis, ke Radio Shack estis fabrikanto de kasedoj, kiel BASF, Sony aŭ TDK, kaj TRS-80 estis la ludtempo. Kial ne?

Tandy/Radio Shack TRS-80 Komputilo

Estas tre probable, ke la koncerna sonregistraĵo, kiun mi donis kiel ekzemplon komence de la artikolo, estis farita per komputilo kiel ĉi tiu:

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Montriĝas, ke ĉi tiu komputilo kaj ĝiaj variaĵoj (Modelo I/Modelo III/Modelo IV, ktp.) estis tre popularaj en sia tempo (kompreneble ne en Rusio). Indas rimarki, ke la procesoro, kiun ili uzis, ankaŭ estis Z80. Informoj pri ĉi tiu komputilo troveblas interrete. multe da informojEn la 80-aj jaroj, informoj pri la komputilo estis disvastigitaj en revuojEstas pluraj nuntempe emuliloj komputiloj por malsamaj platformoj.

Mi elŝutis la emulilon trs80gp Kaj por la unua fojo, mi povis vidi kiel ĉi tiu komputilo funkciis. Kompreneble, la komputilo ne subtenis kolorajn eligojn, kaj la ekranrezolucio estis nur 128x48 rastrumeroj, sed ekzistis multaj etendaĵoj kaj modifoj, kiuj povis pliigi la ekranrezolucion. Ankaŭ ekzistis multaj operaciumvariantoj por ĉi tiu komputilo kaj efektivigoj de la lingvo BASIC (kiu, male al la ZX Spectrum, eĉ ne estis enigita en ROM-on en iuj modeloj; ĉiu varianto povus esti startigita de disketo, same kiel la operaciumo mem).

Mi ankaŭ trovis utileco por konverti sonregistraĵojn al la CAS-formato, kiun subtenas emuliloj, sed pro iu kialo mi ne povis legi la registraĵojn de miaj kasedoj per ili.

Eltrovinte la CAS-dosierformaton (kiu montriĝis nur pec-post-peca kopio de la datumoj de la bendo, kiun mi jam havis, krom la kaplinio kun la sinkroniga bajto), mi faris kelkajn ŝanĝojn al mia programo kaj sukcesis ricevi funkciantan CAS-dosieron ĉe la eligo, kiu funkciis en la emulilo (TRS-80 Modelo III):

Kiel mi reakiris datumojn en nekonata formato de magneta bendo

Mi desegnis la plej novan version de la konverta ilo kun aŭtomata detekto de la unua pulso kaj la distanco inter referencaj pulsoj kiel GEM-pakaĵon, la fontkodo estas havebla ĉe GitHub.

konkludo

La vojaĝo ĝis nun estis fascina vojaĝo en la pasintecon, kaj mi ĝojas, ke mi finfine trovis la respondon. Interalie, mi:

  • Mi eltrovis la datenkonservan formaton en la ZX Spectrum kaj studis la enkonstruitajn ROM-rutinojn por konservi/legi datumojn de sonkasedoj.
  • Mi konatiĝis kun la komputilo TRS-80 kaj ĝiaj variaĵoj, studis la operaciumon, rigardis ekzemplajn programojn, kaj eĉ havis la ŝancon sencimigi en maŝinkodoj (finfine, mi bone konas ĉiujn mnemonikojn de Z80)
  • Mi verkis plenkreskan ilon por konverti sonregistraĵojn al CAS-formato, kiu povas legi datumojn, kiujn ne rekonas la "oficiala" ilo.

fonto: www.habr.com

Aĉetu fidindan gastigadon por retejoj kun DDoS-protekto, VPS-VDS-serviloj 🔥 Aĉetu fidindan retejan gastigadon kun DDoS-protekto, VPS VDS-servilojn | ProHoster