prasejarah
Sebagai pencinta perkakasan retro, saya pernah membeli ZX Spectrum+ daripada penjual di UK. Termasuk dengan komputer itu sendiri, saya menerima beberapa kaset audio dengan permainan (dalam pembungkusan asal dengan arahan), serta program yang dirakam pada kaset tanpa tanda khas. Yang menghairankan, data daripada kaset berusia 40 tahun boleh dibaca dengan baik dan saya dapat memuat turun hampir semua permainan dan program daripadanya.
Walau bagaimanapun, pada beberapa kaset saya dapati rakaman yang jelas tidak dibuat oleh komputer ZX Spectrum. Mereka berbunyi berbeza dan, tidak seperti rakaman dari komputer yang disebutkan, mereka tidak bermula dengan pemuat but ASAS yang pendek, yang biasanya terdapat dalam rakaman semua program dan permainan.
Untuk beberapa waktu ini menghantui saya - saya benar-benar ingin mengetahui apa yang tersembunyi di dalamnya. Jika anda boleh membaca isyarat audio sebagai jujukan bait, anda boleh mencari aksara atau apa sahaja yang menunjukkan asal usul isyarat. Sejenis retro-arkeologi.
Sekarang saya telah pergi jauh dan melihat label kaset itu sendiri, saya tersenyum kerana
jawapan itu ada di depan mata saya selama ini
Pada label kaset kiri adalah nama komputer TRS-80, dan betul-betul di bawah nama pengilang: "Dikilangkan oleh Radio Shack di Amerika Syarikat"
(Jika anda ingin mengekalkan tipu daya sehingga akhir, jangan pergi di bawah spoiler)
Perbandingan isyarat audio
Pertama sekali, mari kita mendigitalkan rakaman audio. Anda boleh mendengar bunyinya:
Dan seperti biasa rakaman dari komputer ZX Spectrum berbunyi:
Dalam kedua-dua kes, pada permulaan rakaman terdapat apa yang dipanggil nada perintis - bunyi dengan frekuensi yang sama (dalam rakaman pertama ia sangat pendek <1 saat, tetapi boleh dibezakan). Nada perintis memberi isyarat kepada komputer untuk bersedia untuk menerima data. Sebagai peraturan, setiap komputer hanya mengenali nada pandu "sendiri" dengan bentuk isyarat dan kekerapannya.
Ia adalah perlu untuk mengatakan sesuatu tentang bentuk isyarat itu sendiri. Sebagai contoh, pada ZX Spectrum bentuknya adalah segi empat tepat:
Apabila nada perintis dikesan, ZX Spectrum memaparkan bar merah dan biru berselang-seli pada sempadan skrin untuk menunjukkan bahawa isyarat telah dikenali. Nada juruterbang tamat nadi segerak, yang memberi isyarat kepada komputer untuk mula menerima data. Ia dicirikan oleh tempoh yang lebih pendek (berbanding dengan nada perintis dan data seterusnya) (lihat rajah)
Selepas nadi penyegerakan diterima, komputer merekodkan setiap kenaikan/kejatuhan isyarat, mengukur tempohnya. Jika tempohnya kurang daripada had tertentu, bit 1 ditulis pada ingatan, jika tidak 0. Bit dikumpulkan ke dalam bait dan proses diulang sehingga N bait diterima. Nombor N biasanya diambil daripada pengepala fail yang dimuat turun. Urutan pemuatan adalah seperti berikut:
- nada perintis
- pengepala (panjang tetap), mengandungi saiz data yang dimuat turun (N), nama fail dan jenis
- nada perintis
- data itu sendiri
Untuk memastikan bahawa data dimuatkan dengan betul, ZX Spectrum membaca apa yang dipanggil bait pariti (bait pariti), yang dikira semasa menyimpan fail dengan XORing semua bait data bertulis. Apabila membaca fail, komputer mengira bait pariti daripada data yang diterima dan, jika hasilnya berbeza daripada yang disimpan, memaparkan mesej ralat "Ralat memuatkan Pita R". Tegasnya, komputer boleh mengeluarkan mesej ini lebih awal jika, semasa membaca, ia tidak dapat mengecam nadi (terlepas atau tempohnya tidak sepadan dengan had tertentu)
Jadi, mari kita lihat rupa isyarat yang tidak diketahui:
Ini adalah nada perintis. Bentuk isyarat adalah berbeza dengan ketara, tetapi jelas bahawa isyarat terdiri daripada denyutan pendek berulang pada frekuensi tertentu. Pada frekuensi pensampelan 44100 Hz, jarak antara "puncak" adalah lebih kurang 48 sampel (yang sepadan dengan frekuensi ~918 Hz). Mari kita ingat angka ini.
Sekarang mari kita lihat serpihan data:
Jika kita mengukur jarak antara denyutan individu, ternyata jarak antara denyutan "panjang" masih ~48 sampel, dan antara yang pendek - ~24. Melihat ke hadapan sedikit, saya akan mengatakan bahawa pada akhirnya ternyata denyutan "rujukan" dengan frekuensi 918 Hz mengikuti secara berterusan, dari awal hingga akhir fail. Ia boleh diandaikan bahawa apabila menghantar data, jika nadi tambahan ditemui di antara denyut rujukan, kami menganggapnya sebagai bit 1, sebaliknya 0.
Bagaimana pula dengan nadi penyegerakan? Mari kita lihat pada permulaan data:
Nada perintis tamat dan data bermula serta-merta. Tidak lama kemudian, selepas menganalisis beberapa rakaman audio yang berbeza, kami dapat menemui bahawa bait pertama data sentiasa sama (10100101b, A5h). Komputer mungkin mula membaca data selepas ia menerimanya.
Anda juga boleh memberi perhatian kepada anjakan nadi rujukan pertama sejurus selepas 1 terakhir dalam bait penyegerakan. Ia ditemui lebih lama kemudian dalam proses membangunkan program pengecaman data, apabila data pada permulaan fail tidak dapat dibaca dengan stabil.
Sekarang mari kita cuba menerangkan algoritma yang akan memproses fail audio dan memuatkan data.
Memuatkan Data
Mula-mula, mari kita lihat beberapa andaian untuk memastikan algoritma mudah:
- Kami hanya akan mempertimbangkan fail dalam format WAV;
- Fail audio mesti bermula dengan nada perintis dan tidak boleh mengandungi senyap pada mulanya
- Fail sumber mesti mempunyai kadar pensampelan 44100 Hz. Dalam kes ini, jarak antara denyut rujukan 48 sampel sudah ditentukan dan kita tidak perlu mengiranya secara pemrograman;
- Format sampel boleh menjadi mana-mana (8/16 bit/titik terapung) - kerana apabila membaca kita boleh menukarnya kepada yang dikehendaki;
- Kami menganggap bahawa fail sumber dinormalkan oleh amplitud, yang sepatutnya menstabilkan hasilnya;
Algoritma bacaan adalah seperti berikut:
- Kami membaca fail ke dalam ingatan, pada masa yang sama menukar format sampel kepada 8 bit;
- Tentukan kedudukan nadi pertama dalam data audio. Untuk melakukan ini, anda perlu mengira bilangan sampel dengan amplitud maksimum. Untuk memudahkan, kami akan mengiranya sekali secara manual. Mari kita simpan ke pembolehubah prev_pos;
- Tambahkan 48 pada kedudukan nadi terakhir (pos := prev_pos + 48)
- Memandangkan meningkatkan kedudukan sebanyak 48 tidak menjamin bahawa kita akan sampai ke kedudukan nadi rujukan seterusnya (kecacatan pita, operasi mekanisme pemacu pita yang tidak stabil, dsb.), kita perlu melaraskan kedudukan nadi pos. Untuk melakukan ini, ambil sekeping kecil data (pos-8;pos+8) dan cari nilai amplitud maksimum padanya. Kedudukan yang sepadan dengan maksimum akan disimpan dalam pos. Di sini 8 = 48/6 ialah pemalar yang diperoleh secara eksperimen, yang menjamin bahawa kami akan menentukan maksimum yang betul dan tidak akan menjejaskan impuls lain yang mungkin berdekatan. Dalam kes yang sangat teruk, apabila jarak antara denyutan adalah lebih kurang daripada atau lebih besar daripada 48, anda boleh melaksanakan pencarian paksa untuk nadi, tetapi dalam skop artikel saya tidak akan menerangkan ini dalam algoritma;
- Pada langkah sebelumnya, ia juga perlu untuk memeriksa sama ada nadi rujukan ditemui sama sekali. Iaitu, jika anda hanya mencari maksimum, ini tidak menjamin bahawa impuls hadir dalam segmen ini. Dalam pelaksanaan terbaru program bacaan saya, saya menyemak perbezaan antara nilai amplitud maksimum dan minimum pada segmen, dan jika ia melebihi had tertentu, saya mengira kehadiran impuls. Persoalannya juga apa yang perlu dilakukan jika nadi rujukan tidak dijumpai. Terdapat 2 pilihan: sama ada data telah tamat dan senyap menyusul, atau ini harus dianggap sebagai ralat bacaan. Walau bagaimanapun, kami akan meninggalkan ini untuk memudahkan algoritma;
- Pada langkah seterusnya, kita perlu menentukan kehadiran nadi data (bit 0 atau 1), untuk ini kita mengambil bahagian tengah segmen (prev_pos;pos) middle_pos sama dengan middle_pos := (prev_pos+pos)/2 dan dalam beberapa kejiranan middle_pos pada segmen (middle_pos-8;middle_pos +8) mari kita mengira amplitud maksimum dan minimum. Jika perbezaan di antara mereka adalah lebih daripada 10, kami menulis bit 1 ke dalam hasilnya, jika tidak 0. 10 ialah pemalar yang diperoleh secara eksperimen;
- Simpan kedudukan semasa dalam prev_pos (prev_pos := pos)
- Ulang bermula dari langkah 3 sehingga kita membaca keseluruhan fail;
- Tatasusunan bit yang terhasil mesti disimpan sebagai satu set bait. Oleh kerana kami tidak mengambil kira bait penyegerakan semasa membaca, bilangan bit mungkin bukan gandaan 8, dan mengimbangi bit yang diperlukan juga tidak diketahui. Dalam pelaksanaan pertama algoritma, saya tidak tahu tentang kewujudan bait penyegerakan dan oleh itu hanya menyimpan 8 fail dengan bilangan bit offset yang berbeza. Salah satu daripadanya mengandungi data yang betul. Dalam algoritma akhir, saya hanya mengeluarkan semua bit sehingga A5h, yang membolehkan saya segera mendapatkan fail output yang betul
Algoritma dalam Ruby, untuk mereka yang berminat
Saya memilih Ruby sebagai bahasa untuk menulis program, kerana... Saya memprogramkannya pada kebanyakan masa. Pilihannya tidak berprestasi tinggi, tetapi tugas untuk membuat kelajuan membaca secepat mungkin tidak berbaloi.
# ΠΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌ 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*")
Keputusan
Setelah mencuba beberapa varian algoritma dan pemalar, saya bernasib baik kerana mendapat sesuatu yang sangat menarik:
Jadi, berdasarkan rentetan aksara, kami mempunyai program untuk memplot graf. Walau bagaimanapun, tiada kata kunci dalam teks program. Semua kata kunci dikodkan sebagai bait (setiap nilai > 80j). Sekarang kita perlu mengetahui komputer dari tahun 80-an yang boleh menyimpan program dalam format ini.
Malah, ia hampir sama dengan program BASIC. Komputer ZX Spectrum menyimpan program dalam format yang lebih kurang sama dalam memori dan menyimpan program ke pita. Untuk berjaga-jaga, saya menyemak kata kunci terhadapnya
Saya juga menyemak kata kunci ASAS Atari yang popular, Commodore 64 dan beberapa komputer lain pada masa itu, yang mana saya dapat mencari dokumentasi, tetapi tidak berjaya - pengetahuan saya tentang jenis komputer retro ternyata tidak begitu luas.
Kemudian saya memutuskan untuk pergi
Komputer Tandy/Radio Shack TRS-80
Kemungkinan besar rakaman audio yang dimaksudkan, yang saya berikan sebagai contoh pada permulaan artikel, dibuat pada komputer seperti ini:
Ternyata komputer ini dan jenisnya (Model I/Model III/Model IV, dsb.) sangat popular pada satu masa (sudah tentu, bukan di Rusia). Perlu diperhatikan bahawa pemproses yang mereka gunakan juga adalah Z80. Untuk komputer ini anda boleh mencari di Internet
Saya memuat turun emulator
saya pun jumpa
Setelah mengetahui format fail CAS (yang ternyata hanya salinan sedikit demi sedikit data daripada pita yang saya sedia ada, kecuali pengepala dengan kehadiran bait penyegerakan), saya membuat beberapa perubahan pada program saya dan dapat mengeluarkan fail CAS yang berfungsi yang berfungsi dalam emulator (TRS-80 Model III):
Saya mereka bentuk versi terkini utiliti penukaran dengan penentuan automatik denyutan pertama dan jarak antara denyut rujukan sebagai pakej PERMATA, kod sumber tersedia di
Kesimpulan
Laluan yang telah kami lalui ternyata menjadi perjalanan yang menarik ke masa lalu, dan saya gembira kerana akhirnya saya menemui jawapannya. Antara lain, saya:
- Saya mengetahui format untuk menyimpan data dalam Spektrum ZX dan mengkaji rutin ROM terbina dalam untuk menyimpan/membaca data daripada kaset audio
- Saya berkenalan dengan komputer TRS-80 dan jenisnya, mengkaji sistem pengendalian, melihat program sampel dan juga berpeluang melakukan penyahpepijatan dalam kod mesin (lagipun, semua mnemonik Z80 sudah biasa bagi saya)
- Menulis utiliti lengkap untuk menukar rakaman audio kepada format CAS, yang boleh membaca data yang tidak dikenali oleh utiliti "rasmi"
Sumber: www.habr.com