背景
作为复古铁的爱好者,我曾经从英国卖家那里购买了ZX Spectrum +。除了计算机本身,我还提供了一些带有游戏的盒式录音带(带说明的原始包装),以及在盒式录音带上录制的程序,没有任何特殊标记。令人惊讶的是,具有40年历史的卡带中的数据是可读的,我能够从中下载几乎所有的游戏和程序。
但是,在某些录像带上,我发现显然不是ZX Spectrum计算机制作的录音。它们听起来完全不同,并且与上述计算机的录音不同,它们并非以简短的BASIC引导加载程序开头,该引导加载程序通常出现在所有程序和游戏的录音中。
一段时间以来,我一直对此感到困扰-我真的很想知道其中隐藏着什么。如果您可以按字节序列读取音频信号,则可以在其中查找字符或表明信号来源的内容。一种复古的考古学。
现在,我一路走来,看看卡带本身的标签,我微笑着,因为
答案一直在我眼前
— TRS-80, : «Manufactured by Radio Shack in USA»
(如果您想一直保持阴谋诡计,请不要将其遮挡住)
音频信号比较
第一步是将录音数字化。您可以听一下它的声音:
和往常一样,ZX Spectrum计算机的录音听起来像:
在这两种情况下,在记录开始时都有一个所谓的导频音 -一种频率的声音(在第一次记录中,它很短<1秒,但可以区分)。提示音会向计算机发出信号,以准备接收数据。通常,每台计算机只能通过波形及其频率识别“其”导频音。
我必须说一下信号形状本身。例如,在ZX Spectrum上,其形状为矩形:
检测到引导音时,ZX Spectrum在屏幕边框上交替显示红色和蓝色条纹,表示已识别信号。导频音以同步脉冲结束,这表示计算机开始接收数据。其特点是持续时间较短(与导频音和后续数据相比)(见图)。
接收到同步脉冲后,计算机会记录信号的每次上升/下降,并测量其持续时间。如果持续时间小于某个限制,则将位1写入内存,否则写入0。将位收集到字节中,然后重复该过程,直到接收到N个字节为止。数字N通常取自下载文件的标题。引导顺序如下:
- 领航音
- 标头(固定长度),包含已加载数据的大小(N),名称和文件类型
- 领航音
- 数据本身
为了确保正确加载数据,ZX Spectrum读取所谓的字节奇偶校验(奇偶校验字节)的最后一个字节,该字节是在对记录数据的所有字节保存文件操作XOR时计算得出的。读取文件时,计算机将从接收到的数据中计算出奇偶校验字节,如果结果与保存的结果不同,则会显示错误消息“ R Tape loading error”。严格来说,如果计算机在读取时无法识别脉冲(错过了脉冲或脉冲的持续时间不符合特定边界),则计算机可以更早地发出此消息,
因此,现在让我们来看看未知信号是什么样的:
这是领航音。波形明显不同,但是您可以看到信号由一定频率的重复短脉冲组成。在44100 Hz的采样率下,“峰”之间的距离大约为48个样本(对应于〜918 Hz的频率)。
现在,让我们看一下数据片段:
如果我们测量单个脉冲之间的距离,结果发现“长”脉冲之间的距离仍然是〜48个样本,而短脉冲之间的距离-〜24个样本。向前走一点,我要说的是,最后结果是,从文件的开头到结尾,频率连续为918 Hz的“参考”脉冲。可以假设,在数据传输期间,如果在参考脉冲之间出现一个附加脉冲,我们将其视为位1,否则为0。
同步脉冲是什么?让我们看一下数据的开头:
引导音结束并且数据立即开始。稍后,在分析了几种不同的音频记录后,我们发现第一个数据字节始终是相同的(10100101b,A5h)。计算机收到数据后可能会开始读取数据。
您还可以注意同步字节中最后一个参考脉冲之后的第一个参考脉冲的移位。当无法稳定读取文件开头的数据时,在开发用于数据识别的程序的过程中发现了很多时间。
现在,让我们尝试描述一种将处理音频文件并加载数据的算法。
加载数据中
首先,让我们看一些假设,以免使算法复杂化:
- 我们将仅考虑WAV格式的文件;
- 音频文件必须以领航音开头,并且开头不得包含静音
- 源文件的采样率必须为44100 Hz。在这种情况下,已经确定了48个样本的参考脉冲之间的距离,因此我们无需通过程序进行计算;
- 样本格式可以是任何格式(8/16位/浮点数)-因为在读取时,我们可以将其转换为所需的格式;
- 我们假设原始文件的幅度被归一化,这将使结果稳定。
读取算法如下:
- 我们将文件读入内存,同时将样本格式转换为8位;
- 确定音频数据中第一个脉冲的位置。为此,您需要计算最大振幅的样本数。为简单起见,让我们手动对其进行一次计数。让我们将其保存到prev_pos变量中;
- 在最后一个脉冲的位置加上48(位置:= prev_pos + 48)
- 48 , ( , ), pos. (pos-8;pos+8) . , , pos. 8 = 48/6 — , , . , 48, , ;
- , . , , . , , . , . 2 : , . ;
- ( 0 1), (prev_pos;pos) middle_pos middle_pos := (prev_pos+pos)/2 middle_pos (middle_pos-8;middle_pos+8) . 10, 1 0. 10 — ;
- prev_pos (prev_pos := pos)
- 3, ;
- . - , 8, . - 8 . . A5h,
Ruby,
Ruby, .. . , .
# 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*")
尝试了几种算法和常量的变体后,我很幸运地得到了一些非常有趣的东西:
因此,根据字符串判断,我们有一个用于绘制图形的程序。但是,程序文本中没有关键字。所有关键字均编码为字节(每个值> 80h)。现在我们需要找出80年代的哪台计算机可以这种格式保存程序。
这实际上与BASIC程序非常相似。 ZX Spectrum计算机以大致相同的格式存储在内存中,并将程序保存到磁带上。以防万一,我对照表检查了关键字。但是,结果显然是负面的。
我还检查了当时流行的Atari计算机,Commodore 64和其他几台计算机的BASIC关键字,设法找到了相关文档,但无济于事-我对复古计算机类型的了解并不广泛。
然后,我决定浏览一下清单,然后我的目光投向了Radio Shack和TRS-80计算机的制造商。这些名字写在我桌上的录音带的标签上!毕竟,我以前都不知道这些名称,也不熟悉TRS-80计算机,所以在我看来,Radio Shack是录音带的制造商,例如BASF,Sony或TDK,而TRS-80是回放的持续时间。为什么不?
电脑Tandy / Radio Shack TRS-80
我在本文开头举例说明的音频录制很有可能是在这样的计算机上进行的:
事实证明,该计算机及其变体(I型/ III型/ IV型等)在他们的计算机中非常受欢迎。时间(当然不是在俄罗斯)。值得注意的是,它们中使用的处理器也是Z80。在Internet上的这台计算机上可以找到很多信息。在1980年代,有关计算机的信息在杂志上发行。目前,有几种用于不同平台的计算机仿真器。
我下载了trs80gp模拟器我第一次能够看到这台计算机的工作原理。当然,计算机不支持彩色输出,屏幕分辨率仅为128x48像素,但是有许多扩展和修改可以提高屏幕分辨率。该计算机的操作系统也有很多选项,以及实现BASIC语言的选项(与ZX Spectrum不同,在某些型号中,该语言甚至没有“刷入” ROM,并且任何选项都可以从软盘以及操作系统本身启动)
。我找到了一个将音频记录转换为CAS格式的实用程序,该程序受仿真器支持,但是由于某些原因,我无法在他们的帮助下从盒式磁带中读取记录。
弄清楚了CAS文件的格式(原来只是磁带上数据的按位复制,除了带同步字节的标头外,我已经掌握了它),我对程序进行了几处更改,并能够在输出端得到一个有效的CAS文件。它在仿真器中工作(TRS-80 Model III):
该实用程序的最新版本可自动检测第一个脉冲和我设计为GEM软件包的参考脉冲之间的距离进行转换,源代码可在Github上获得。
结论
穿越的道路原来是过去的激动人心的旅程,我很高兴最终找到了解决方案。除其他外,我:
- 我弄清楚了在ZX Spectrum中保存数据的格式,并研究了用于从音频磁带保存/读取数据的内置ROM例程。
- 我熟悉TRS-80计算机及其品种,研究了操作系统,查看了程序示例,甚至有机会调试了机器代码(毕竟,所有Z80助记符对我来说都是熟悉的)
- 我编写了一个完整的实用程序,用于将录音转换为CAS格式,该程序可以读取“官方”实用程序无法识别的数据