PDS 项目第八部分:音频加载与 Amstrad 目标板实测

我心想,既然已经入手了一台 Amstrad CPC 464,不妨好好研究一下 Amstrad Target 板。不过在那之前,我先来简单聊一聊如何从音频文件中加载内容。

转而使用真实音频文件

我原本想为 TAP 文件生成一个音频文件,以便在 ZX Spectrum 上使用,而将文件导出为 WAV 格式也相对简单。然而,要找到一种方法来播放这个文件,却比我预想的要复杂得多……

起初,我打算直接用 TZXduino 播放该文件,再用手机或其他设备将其录制下来——结果确实奏效了,但声音有些杂音,而且文件始终无法成功加载。

后来,我发现了 https://www.igormaznitsa.com/tap2wav/index.html。

这款工具非常棒,能够生成极其纯净的信号。我最初找到的工具未能正确将波形的“零点”居中,因此一度以为这可能会带来问题,但这款工具则能清晰地完成所有操作;当信号在音频编辑器中打开时,我们就能清楚地看到每一步的精确度。

不过,即便如此,文件仍然无法加载。到目前为止,我已经尝试过以下几种方法:

  • 使用配备 USB-C 转 3.5mm 插孔适配器的智能手机。
  • 使用一款没有特殊功能的普通手机,配备物理耳机插孔。
  • 使用那些老旧、本就几乎毫无用处的 AAA 电池供电 MP3 播放器(我试过好几款)。

至于文件本身,我尝试过以下几种格式:

  • WAV 和 MP3。
  • 对信号进行放大、缩小,或以其他方式调整音量。
  • 使用噪声门滤波器。

但无论我怎么尝试,都没有任何一种方案能够成功。有一次,我甚至让第一阶段的加载器成功识别出了屏幕上的文件名,可紧接着却出现了“R 磁带加载错误”的提示。

在网上搜索后,我得到了一些解答。原来,现代数字播放器所具备的输出电平,根本无法驱动 ZX Spectrum。如果系统拥有较大的音量范围,或许还能勉强实现,但很可能没有任何一款设备能与“复古”式的小型磁带录音机相媲美。

斯图尔特·拉塞尔 在 Mastodon 上将我引到了这个链接,那里提供了一个非常巧妙的解决方案:https://retrocomputing.stackexchange.com/questions/773/loading-zx-spectrum-tape-audio-in-a-post-cassette-world。

其基本思路是:使用一块电池供电的播放器,这样它就不会与 Spectrum 的公共 GND 连接,而是通过差分立体声信号进行连接。接着,借助一个立体声插孔,将信号连接到 Spectrum 中的单声道插孔,其中一路作为 GND,另一路则作为信号。

这样一来,信号的强度就会被翻倍。这真是颇具创意的思维转变!

目前我还没有真正尝试这一方案,说实话,我其实更希望找到一种更为传统的解决方案,但归根结底,要与 TZXDuino/MaxDuino 这类方案竞争,还是相当困难的。

Amstrad CPC Target 板

我原本很想尝试让 Amstrad 板正常运行,但要做到这一点,我缺了一件至关重要的设备。我并没有一台 Amstrad 8 位电脑——直到现在。

最近在 复古集体 的一场复古商品促销活动中,我终于淘到了一台带有彩色显示器的 Amstrad CPC 464,而且看起来它真的能正常工作,这简直太神奇了。

于是,我开始按照在 第五部分 中为 Spectrum 板所采取的步骤,重复进行了一些操作。

关键要点:

  • Amstrad 有与 DL0、DL1 和 DL2 监视器代码相对应的版本,适用于 Amstrad CPC。
  • Amstrad Target 板采用端口 &FBEC- 和 FBEF 作为 PIO 数据和控制通道。
  • 除此之外,其他部分基本上与 Spectrum 版本相同。

要将监视器代码转化为对我真正可用的形式,需要完成以下步骤:

  • 对源代码进行同样的调整(如修改 JP、添加标签,并移除 PDS 特定的指令)。
  • 利用在线 Z80 汇编器将代码汇编成形。
  • 将 HEX 或二进制输出转换为我可以用于 Amstrad 的格式。

然而,我在这里遇到了第一个难题:Amstrad CPC 464 并没有外置磁带端口,因此我无法将其与 TZXDuino 配合使用。TZXDuino 支持 CDT 磁带格式——这是许多 Amstrad 图像所采用的格式——但这里却没有合适的插槽可以插入磁带。

网上有不少项目展示了如何为后期型号的 Amstrad 添加一个典型的 5 引脚 DIN 磁带端口插座,不过到目前为止,我还没有这样的设备。

于是,我又想到了另一种办法:利用之前的一些技术,制作一个可以实际写入磁带的音频文件。尽管我的磁带驱动器(也就是“数据记录仪”)的 FF 和 REV 功能都正常运作,但似乎 Play 无法启动传输装置,使转盘移动。

所以,这个问题我还需要在未来的某个时刻去解决。

目前,如果我想尝试一下,只能回到用 BASIC 机器码加载器进行编写。

Amstrad 的 BASIC 语言比 Spectrum 更加复杂,因此我可以直接使用 HEX 值。这意味着,将 HEX 记录格式加以简化,只需稍作重复粘贴,就能将数值以 HEX 码的形式纳入 BASIC 程序中,如下所示:

:108000003EFF01EEFBED79ED790CED790USED79AA
:108010000EEF3E3FED791640F3CD60807BFEB4CA93
:108020004380FEB7CA3980FEB5C21980CD60806337
:10803000CD60806B011980C5E9CD6080067F4BED76
:108040004918D6CD608063CD60806BCD608043CD14
:1080500060804BCD608073230B78B1C2538018B918
:10806000C501EDFBED78AA0FDA64800DED580C7AAE
:07807000ED79EE8157C1C953
:00000001FF

5 符号之后的 256:内存 &7FFF:符号之后的 240
10 LET a=&8000
20 READ n: POKE a,n
30 LET a=a+1
40 GOTO 20
100 DATA &3E,&FF,&01,&EE, &FB,&ED,&79,&ED
105 DATA &79,&0C,&ED,&79, &0E,&ED,&ED,&79
110 DATA &0E,&EF,&3E,&3F, &ED,&79,&16,&40
115 DATA &F3,&CD,&60,&80, &7B,&FE,&B4,&CA
120 DATA &43,&80,&FE,&B7, &CA,&39,&80,&FE
125 DATA &B5,&C2,&19,&80, &CD,&60,&80,&63
130 DATA &CD,&60,&80,&6B, &01,&19,&80,&C5
135 DATA &E9,&CD,&60,&80, &06,&7F,&4B,&ED
140 DATA &49,&18,&D6,&CD, &60,&80,&63,&CD
145 DATA &60,&80,&6B,&CD, &60,&80,&43,&CD
150 DATA &60,&80,&4B,&CD, &60,&80,&73,&23
155 DATA &0B,&78,&B1,&C2, &53,&80,&18,&B9
160 DATA &C5,&01,&ED,&FB, &ED,&78,&AA,&0F
165 DATA &DA,&64,&80,&0D, &ED,&58,&0C,&7A
170 DATA &ED,&79,&EE,&81, &57,&C1,&C9

运行
CALL &8000

在上述代码中,我们可以看到 DL0 的 HEX 记录格式——去除了 PDS 特定的细节——其起始地址为 0x8000。接下来是一个等效的 BASIC 加载器,其中包含了用于存储 HEX 值的数据语句,去除了行头和校验和。

随后是两条指令,我需要通过这些指令来运行加载器,然后再调用代码。

显然,BASIC 程序的第一行,正是用来限制 BASIC 使用的内存空间,并为用户机器码程序预留空间。

我在编写完所有这些代码并边写边检查,最后又进行了双重确认。然而,结果依然一片空白。PC 端与之前完全一致,我本打算再次尝试填充屏幕内存(屏幕内存的起始地址似乎是 0xC000),但当我试图与目标端连接时,却遭遇了可怕的“计算机未接收”错误。

无奈之下,我只好重置 Amstrad,重新尝试弄清究竟发生了什么。

按照我在 第五部分 中所采用的流程(并结合 第六部分 提供的诊断板),我首先尝试使用 OUT 指令,直接向 Amstrad BASIC 的 Z80 PIO 写入数据:

OUT &FBEF, 255 : REM B 端口控制
OUT &FBEF, 63 : REM B 端口的第 6、7 位作为输出
OUT &FBED, 128 : REM B 端口的第 7 位
OUT &FBED, 0

原本应该让目标端的 LED 从亮到灭。然而,我却什么也没发生。

此时,我回过头来仔细核对了原理图和地址解码,确保自己清楚地知道原本应该发生什么。原理图的相关部分如下所示:

对于地址解码,A0 和 A1 的作用与之前相同——它们分别选择 A 端口和 B 端口,负责控制和数据传输。IO 地址由 A4 和 A10 通过与 /CE 的或门决定。

其解码过程如下:

我们可以看到,唯一能使 /CE 为低电平(从而激活 Z80 PIO)的组合,是当 A4 和 A10 都为 0 时。不过,这也导致地址解码变得十分混乱:忽略了 A0 和 A1,其他 10 条地址线中的任意一条都可以取任何值,而解码依然会触发。

通常的做法是使用“全高电平”的地址,这样我们就得到了前缀 0xFBEC,如下所示:

0xFBEC = b1111 1011 1110 11xx

此外,我还思考过,启用开关是如何连接到PIO的 /IORQ 线——在两种情况下,一次使用直接的 10K 上拉电阻,另一次则使用 1M 上拉电阻,然后连接到边缘连接器 /IORQ。

我还注意到,时钟信号连接到边缘连接器的 CLK4;同时,只有 /RD 与 /M1 一起连接。不过,除此之外,整体并没有什么特别奇怪的地方。

这时,我拿起了示波器,一边运行 OUT 指令,一边开始仔细观察。有一次,我不小心碰倒了电路板,屏幕上突然出现了一堆乱七八糟的字符,仿佛我在键盘上随机输入了字符一样,于是我开始怀疑电路板是否安装得当。

关掉电源,重新插好电路板,再次尝试执行 OUT 指令,这一次,我确实看到了一些 LED 开始被控制。也许只是因为物理连接没有完全到位而已。

此时,我决定再次重新输入 BASIC 加载器的代码。

尽管我仍然没有收到任何来自 PC PDS 的屏幕填充响应,但更重要的是,我也没有再遇到“计算机未接收”的错误。当我把示波器连接到 PC 与目标端之间的控制线路,以及目标端与 PC 之间的控制线路时,我可以看到两者都在根据通信情况不断切换状态。

因此,在此阶段,我可以相当肯定地说,Amstrad CPC Target 板已经正常工作,只是由于某种原因(很可能是由于我对 Amstrad 工作原理了解不足),我始终未能在 CPC 中通过访问内存产生任何可观测的效果。

我仔细检查了监视器代码,发现并没有任何理由说明它不能像 Spectrum 代码那样工作。我注意到,Spectrum 版本在开始时似乎会禁用中断,而 Amstrad 的版本也是如此。

不过,这会不会导致显示更新停止呢?我觉得不会,但还需要进一步的研究。

Target 在 PDSZ80 中的类型

后来,我意识到 PC PDSZ80 软件中有一个配置选项,可以用来设置目标类型。所以在第三次输入 BASIC 加载器之前,我先做了一些相关阅读。

根据 PDS 手册的说明:

手册中并没有明确指出不同模式之间究竟有何不同——协议是否有所不同?于是,我在 PDS Z80 手册中进一步查找,找到了关于“P”命令的相关信息:

而在稍后的章节中,当讨论配置选项时:

虽然曾经有一丝希望,仿佛我忘了什么简单的事项——比如告诉 PDSZ80 与 Amstrad 通信——但读完上述内容后,我并不觉得这会带来任何改变。

至少,对我来说,这还不够重要,不足以让我再次输入加载器来检查。

结论

我认为,我已经证明了电路板本身,以及可能提供的代码,都是有效的——而这正是我最初的主要目的。

遗憾的是,此时我对 Amstrad 的了解还远远不够,无法真正弄清如何以有用的方式驱动 PDS。因此,在这方面,我还有不少工作要做。

不过,我暂时就到这里吧。如果我发现了新的进展,我会回来继续更新相关内容。

Kevin

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论