Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

prehistoarje

As leafhawwer fan retro-hardware, kocht ik ienris in ZX Spectrum + fan in ferkeaper yn it Feriene Keninkryk. Ynbegrepen mei de kompjûter sels krige ik ferskate audio-kassettes mei spultsjes (yn 'e orizjinele ferpakking mei ynstruksjes), en ek programma's opnommen op kassetten sûnder spesjale markearring. Ferrassend genôch wiene gegevens fan 40 jier âlde kassetten goed te lêzen en koe ik hast alle spultsjes en programma's fan har downloade.

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

Op guon kassetten fûn ik lykwols opnames dy't dúdlik net makke binne troch de ZX Spectrum-kompjûter. Se klonk folslein oars en, yn tsjinstelling ta de opnames fan de neamde kompjûter, se begûn net mei in koarte BASIC bootloader, dy't meastal oanwêzich is yn de opnames fan alle programma en games.

Foar in skoft spoeke dit my - ik woe echt útfine wat der yn har ferburgen wie. As jo ​​​​it audiosinjaal lêze kinne as in folchoarder fan bytes, kinne jo sykje nei tekens as alles wat de oarsprong fan it sinjaal oanjout. In soarte fan retro-argeology.

No't ik de hiele wei gien bin en nei de etiketten fan de kassetten sels sjoch, glimkje ik om't

it antwurd lei my de hiele tiid krekt foar eagen
Op it label fan 'e linker kassette stiet de namme fan' e kompjûter TRS-80, en krekt ûnder de namme fan 'e fabrikant: "Produsearre troch Radio Shack yn 'e Feriene Steaten"

(As jo ​​​​de yntrige oant it ein wolle hâlde, gean dan net ûnder de spoiler)

Fergeliking fan audio sinjalen

Lit ús earst de audio-opnames digitalisearje. Jo kinne harkje nei hoe't it klinkt:


En lykas gewoanlik klinkt de opname fan 'e ZX Spectrum-komputer:


Yn beide gefallen is der oan it begjin fan de opname in saneamde pilot toan - in lûd fan deselde frekwinsje (yn 'e earste opname is it heul koart <1 sekonde, mar is te ûnderskieden). De pilottoan sinjalearret de kompjûter om ta te rieden op it ûntfangen fan gegevens. Yn 'e regel erkent elke kompjûter allinich syn "eigen" pilottoan troch de foarm fan it sinjaal en syn frekwinsje.

It is nedich om wat te sizzen oer de sinjaalfoarm sels. Bygelyks, op it ZX Spectrum is syn foarm rjochthoekich:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

As in pilottoan wurdt ûntdutsen, toant it ZX Spectrum ôfwikseljende reade en blauwe balken op 'e grins fan it skerm om oan te jaan dat it sinjaal erkend is. Pilot toan einiget synchro puls, dy't de kompjûter sinjalearret om gegevens te ûntfangen. It wurdt karakterisearre troch in koartere doer (yn ferliking mei de pilottoan en folgjende gegevens) (sjoch figuer)

Nei't de syngronisaasjepuls is ûntfongen, registrearret de kompjûter elke opkomst / fal fan it sinjaal, en mjit de doer. As de doer is minder as in bepaalde limyt, wurdt bit 1 skreaun nei it ûnthâld, oars 0. De bits wurde sammele yn bytes en it proses wurdt werhelle oant N bytes wurde ûntfongen. It nûmer N wurdt normaal nommen út 'e koptekst fan it ynladen bestân. De folchoarder fan it laden is as folget:

  1. pilot toan
  2. header (fêste lingte), befettet de grutte fan de ynladen gegevens (N), triemnamme en type
  3. pilot toan
  4. de gegevens sels

Om der wis fan dat de gegevens wurdt laden korrekt, de ZX Spectrum lêst de saneamde pariteit byte (parity byte), dat wurdt berekkene by it bewarjen fan in triem troch XORing alle bytes fan de skreaune gegevens. By it lêzen fan in triem, de kompjûter berekkent de parity byte út de ûntfongen gegevens en, as it resultaat ferskilt fan de bewarre ien, toant de flater berjocht "R Tape laden flater". Strikt sjoen kin de kompjûter dit berjocht earder útjaan as er by it lêzen gjin pols herkent (mist of syn doer komt net oerien mei bepaalde grinzen)

Dat, lit ús no sjen hoe't in ûnbekend sinjaal derút sjocht:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

Dit is de pilot toan. De foarm fan it sinjaal is signifikant oars, mar it is dúdlik dat it sinjaal bestiet út it werheljen fan koarte pulsen fan in bepaalde frekwinsje. By in samplingfrekwinsje fan 44100 Hz is de ôfstân tusken de "pieken" sawat 48 samples (wat oerienkomt mei in frekwinsje fan ~918 Hz Lit ús dizze sifer ûnthâlde).

Litte wy no nei it datafragmint sjen:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

As wy de ôfstân mjitte tusken yndividuele pulsen, docht bliken dat de ôfstân tusken "lange" pulsen noch ~48 samples is, en tusken koarte - ~24. In bytsje foarút sjen, sil ik sizze dat it úteinlik die bliken dat "referinsje" pulsen mei in frekwinsje fan 918 Hz kontinu folgje, fan it begjin oant it ein fan 'e triem. Der kin oannommen wurde dat by it oerdragen fan gegevens, as der in ekstra puls wurdt tsjinkaam tusken de referinsjepulsen, wy it beskôgje as bit 1, oars 0.

Hoe sit it mei de syngronisaasjepuls? Litte wy nei it begjin fan 'e gegevens sjen:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

De pilottoan einiget en de gegevens begjinne fuortendaliks. Efkes letter, nei it analysearjen fan ferskate ferskillende audio-opnames, koene wy ​​ûntdekke dat de earste byte fan gegevens altyd itselde is (10100101b, A5h). De kompjûter kin begjinne mei it lêzen fan gegevens nei it ûntfangst.

Jo kinne ek betelje omtinken oan de ferskowing fan de earste referinsje puls fuortendaliks nei de lêste 1e yn de syngronisaasje byte. It waard ûntdutsen folle letter yn it proses fan it ûntwikkeljen fan in gegevens erkenning programma, doe't de gegevens oan it begjin fan de triem koe net wurde lêzen stabyl.

Litte wy no besykje in algoritme te beskriuwen dat in audiobestân ferwurket en gegevens sil laden.

It laden fan gegevens

Litte wy earst nei in pear oannames sjen om it algoritme ienfâldich te hâlden:

  1. Wy sille allinne beskôgje triemmen yn WAV formaat;
  2. It audiobestân moat begjinne mei in pilottoan en moat oan it begjin gjin stilte befetsje
  3. De boarne triem moat in sampling rate fan 44100 Hz. Yn dit gefal is de ôfstân tusken de referinsjepulsen fan 48 samples al bepaald en wy hoege it net programmatysk te berekkenjen;
  4. It stekproefformaat kin elk wêze (8/16 bits / driuwend punt) - om't wy it by it lêzen kinne konvertearje nei de winske;
  5. Wy geane derfan út dat de boarne triem wurdt normalisearre troch amplitude, dat moat stabilisearje it resultaat;

It lêsalgoritme sil as folgjend wêze:

  1. Wy lêze de triem yn it ûnthâld, tagelyk it konvertearjen fan it sampleformaat nei 8 bits;
  2. Bepale de posysje fan 'e earste puls yn' e audiogegevens. Om dit te dwaan, moatte jo it oantal fan 'e stekproef mei de maksimale amplitude berekkenje. Foar ienfâld sille wy it ien kear mei de hân berekkenje. Litte wy it bewarje yn 'e fariabele prev_pos;
  3. Foegje 48 ta oan de posysje fan 'e lêste puls (pos := prev_pos + 48)
  4. Sûnt it fergrutsjen fan de posysje troch 48 net garandearje dat wy sille komme ta de posysje fan de folgjende referinsje pols (tape defekten, ynstabyl wurking fan de tape drive meganisme, ensfh), Wy moatte oanpasse de posysje fan de pos pols. Om dit te dwaan, nim in lyts stikje gegevens (pos-8; pos + 8) en fyn de maksimale amplitudewearde derop. De posysje dy't oerienkomt mei it maksimum wurdt opslein yn pos. Hjir 8 = 48/6 is in eksperiminteel krigen konstante, dy't garandearret dat wy sille bepale de krekte maksimum en sil gjin ynfloed op oare ympulsen dy't kin wêze tichtby. Yn hiel minne gefallen, doe't de ôfstân tusken pulses is folle minder as of grutter as 48, kinne jo útfiere in twongen sykjen nei in puls, mar binnen it ramt fan it artikel Ik sil net beskriuwe dit yn it algoritme;
  5. By de foarige stap soe it ek nedich wêze moatte om te kontrolearjen dat de referinsjepuls hielendal fûn is. Dat is, as jo gewoan sykje nei it maksimum, dit garandearret net dat de ympuls oanwêzich is yn dit segmint. Yn myn lêste ymplemintaasje fan it lêsprogramma kontrolearje ik it ferskil tusken de maksimale en minimale amplitudewearden op in segmint, en as it in bepaalde limyt giet, tel ik de oanwêzigens fan in ympuls. De fraach is ek wat te dwaan as de referinsjepuls net fûn wurdt. D'r binne 2 opsjes: of de gegevens binne beëinige en stilte folget, of dit moat wurde beskôge as in lêsflater. Wy sille dit lykwols weilitte om it algoritme te ferienfâldigjen;
  6. By de folgjende stap moatte wy de oanwêzigens fan in gegevenspuls (bit 0 of 1) bepale, dêrfoar nimme wy it midden fan it segmint (prev_pos;pos) middle_pos gelyk oan middle_pos := (prev_pos+pos)/2 en yn guon buert fan middle_pos op it segment (middle_pos-8; middle_pos +8) lit ús berekkenje de maksimum en minimale amplitude. As it ferskil tusken harren is mear as 10, wy skriuwe bit 1 yn it resultaat, oars 0. 10 is in konstante krigen eksperiminteel;
  7. Bewarje de hjoeddeistige posysje yn prev_pos (prev_pos := pos)
  8. Werhelje begjinnend fan stap 3 oant wy it hiele bestân lêze;
  9. De resultearjende bitarray moat wurde bewarre as in set fan bytes. Om't wy de syngronisaasjebyte net rekken holden by it lêzen, kin it oantal bits net in mearfâld fan 8 wêze, en de fereaske bitoffset is ek ûnbekend. Yn 'e earste ymplemintaasje fan it algoritme wist ik net oer it bestean fan' e syngronisaasjebyte en haw ik dêrom gewoan 8 bestannen bewarre mei ferskate oantallen offsetbits. Ien fan harren befette juste gegevens. Yn it definitive algoritme ferwiderje ik gewoan alle bits oant A5h, wêrtroch't ik fuortendaliks it juste útfierbestân kin krije

Algoritme yn Ruby, foar belangstellenden
Ik keas Ruby as taal foar it skriuwen fan it programma, omdat... Ik programmearje der meastentiids op. De opsje is net hege prestaasjes, mar de taak om de lêssnelheid sa rap mooglik te meitsjen is it net wurdich.

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

resultaat

Nei't ik ferskate farianten fan it algoritme en konstanten besocht hie, hie ik it gelok om wat ekstreem ynteressant te krijen:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

Dat, te beoardieljen nei de karakterstrings, hawwe wy in programma foar it plotjen fan grafiken. D'r binne lykwols gjin kaaiwurden yn 'e programmatekst. Alle kaaiwurden wurde kodearre as bytes (elke wearde> 80h). No moatte wy útfine hokker kompjûter út 'e jierren '80 koe bewarje programma yn dit formaat.

Yn feite is it heul gelyk oan in BASIC-programma. De kompjûter ZX Spectrum bewarret programma's yn sawat itselde formaat yn it ûnthâld en bewarret programma's op tape. Krekt foar it gefal, ik kontrolearre de kaaiwurden tsjin tafel. It resultaat wie lykwols fansels negatyf.

Ik kontrolearre ek de BASIC-kaaiwurden fan 'e populêre Atari, Commodore 64 en ferskate oare kompjûters fan dy tiid, wêrfoar ik dokumintaasje koe fine, mar sûnder sukses - myn kennis fan 'e soarten retro-kompjûters die bliken net sa breed te wêzen.

Doe besleat ik om te gean de list, en doe foel myn blik op de namme fan de fabrikant Radio Shack en de TRS-80 kompjûter. Dit binne de nammen dy't op 'e etiketten stiene fan 'e kassetten dy't op myn tafel leine! Ik koe dizze nammen net earder en wie net bekend mei de TRS-80-kompjûter, dus it like my dat Radio Shack in audio-kassettefabrikant wie lykas BASF, Sony of TDK, en de TRS-80 wie de ôfspieltiid. Wêrom net?

Kompjûter Tandy / Radio Shack TRS-80

It is heul wierskynlik dat de audio-opname yn kwestje, dy't ik as foarbyld joech oan it begjin fan it artikel, makke is op in kompjûter lykas dit:

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

It die bliken dat dizze kompjûter en har farianten (Model I / Model III / Model IV, ensfh.) Op ien kear tige populêr wiene (fansels net yn Ruslân). It is opmerklik dat de prosessor se brûkten ek wie Z80. Foar dizze kompjûter kinne jo fine op it ynternet in protte ynformaasje. Yn 'e jierren '80 waard kompjûterynformaasje ferspraat yn tydskriften. Op it stuit binne der ferskate emulators kompjûters foar ferskate platfoarms.

Ik haw de emulator ynladen trs80gp en foar de earste kear koe ik sjen hoe't dizze kompjûter wurke. Fansels stipe de kompjûter gjin kleurútfier, de skermresolúsje wie mar 128x48 piksels, mar d'r wiene in protte útwreidingen en oanpassingen dy't de skermresolúsje ferheegje kinne. D'r wiene ek in protte opsjes foar bestjoeringssystemen foar dizze kompjûter en opsjes foar it ymplementearjen fan 'e BASIC-taal (dy't, yn tsjinstelling ta it ZX Spectrum, yn guon modellen net iens yn ROM "flashed" waard en elke opsje koe wurde laden fan in diskette, krekt as it OS sels)

Ik fûn ek nut om audio-opnames te konvertearjen yn CAS-formaat, dat wurdt stipe troch emulators, mar om ien of oare reden wie it net mooglik om opnames fan myn kassetten te lêzen mei har.

Nei't ik it CAS-bestânsformaat útfûn (dat bliek gewoan in bytsje-by-bit kopy te wêzen fan de gegevens fan 'e tape dy't ik al by de hân hie, útsein de koptekst mei de oanwêzigens fan in syngronisaasjebyte), makke ik in pear wizigingen oan myn programma en koe in wurkjende CAS-bestân útfiere dy't wurke yn 'e emulator (TRS-80 Model III):

Hoe ik gegevens weromhelle yn in ûnbekend formaat fan magnetyske tape

Ik ûntwurp de lêste ferzje fan it konverzje-hulpprogramma mei automatyske bepaling fan 'e earste puls en de ôfstân tusken referinsjepulsen as in GEM-pakket, de boarnekoade is beskikber by Github.

konklúzje

It paad dat wy hawwe reizge blykte in boeiende reis nei it ferline te wêzen, en ik bin bliid dat ik úteinlik it antwurd fûn. Ik haw ûnder oare:

  • Ik fûn it formaat foar it bewarjen fan gegevens yn it ZX Spectrum en studearre de ynboude ROM-routines foar it bewarjen / lêzen fan gegevens fan audiokassetten
  • Ik kaam yn 'e kunde mei de kompjûter TRS-80 en syn farianten, studearre it bestjoeringssysteem, seach nei foarbyldprogramma's en hie sels de kâns om debuggen yn masinekoades te dwaan (ommers, alle Z80-mnemonics binne my bekend)
  • Skreau in folweardich hulpprogramma foar it konvertearjen fan audio-opnames nei CAS-formaat, dat gegevens kin lêze dy't net erkend wurde troch it "offisjele" hulpprogramma

Boarne: www.habr.com

Keapje betroubere hosting foar siden mei DDoS-beskerming, VPS VDS-tsjinners 🔥 Keapje betroubere websidehosting mei DDoS-beskerming, VPS VDS-tsjinners | ProHoster