首页 时政热点 科技头条 智能AI 安全攻防 数码硬件 开发者生态 汽车 游戏 社会热点 开源推荐 医疗健康 归档 标签 关于

ZX 频谱系统之旅:声音

摘要

Now that the shooting gallery is done and dusted, it’s time to turn to a piece of our initial tour of the ZX Spectrum that has been left untouched: sound. There isn’t a whole lot new here compared to ...

the that and our The Spectrum bit but have systems
2026-08-04 1 阅读 约6分钟阅读 ibobev
分享:
字号:
现在射击场已经完工并除尘,是时候转向我们最初的 ZX Spectrum 之旅中未触及的部分了:声音。与之前在其他平台上出现的内容相比,这里并没有太多新内容,但这反过来意味着我们解决方案的核心原则是预先制定的;我们只需要添加 Spectrum 特定的糖霜即可。主线 Spectrum 系统提供两种声音选项: 软件控制下的 1 位蜂鸣器。所有系统上都存在这种情况,并且底层硬件与我们在 Apple II 或 PC 扬声器上看到的没有什么不同。 General Instrument AY-3-8910 合成器芯片存在于 128K Spectrum 及以上的所有型号中,并可用作 16K 和 48K 变体的外围设备。它们与 Atari ST 中的基本相同,但时钟不同,而且我们没有内置 BIOS 调用来与其通信。这是我们一般调查的一部分,就像我们的文本模式和位图模式调查一样,因此我们的目标将是适度的;我们将以三个为我们演奏 C 大调音阶的程序来结束本文。通过 BIOS 使用蜂鸣器 1 位蜂鸣器与 Apple II 的蜂鸣器非常相似; I/O 端口上的 $10 位可以设置或清除以设置扬声器的状态。通过仔细计时切换该位之间的间隔来产生特定的音调。我们稍后会编写执行此操作的代码,但我们可以推迟,因为我们不必自己执行此操作 - BASIC 中的 BEEP 命令已经管理了此操作,并且它通过转发到我们自己可以使用的通用例程来实现这一点。 BEEPER 例程位于内存位置 $03B5,老实说它比我为 6502 系统编写的版本更聪明。 HL 寄存器指定以 4 个周期为单位的延迟时间(无操作的长度),它使用一种与我们用于可编程周期精确 6502 延迟的技术不同的技术来获取该精度。 DE寄存器指定了音符的持续时间,这有点不方便,因为它实际上指定了音符应该具有的波长数。长度相同但音高不同的音符需要不同的时长代码。不过,BEEP 命令本身依赖于以中音 C 的半音形式告知音符,并以秒为单位测量持续时间。我们可以看看它自己的实现,在 $03F8 ,看看如何获​​取我们需要的值。这是通过系统的浮点引擎完成的,因此我们实际上从注释中得到了两者的简洁公式: f Hz 频率的频率代码 HL 由公式 437500/f – 30.125 提供。频率为 f Hz、持续时间为 t 秒的音符的持续时间代码 DE 就是 f×t 。 Python 的几行让我们将这些应用到 C 大调音阶的频率,为我们提供频率和持续时间代码以提供给例程。这是我们的播放程序: org $7000 ld b,8 ;弹奏 8 个音符 ld hl,scale ;从指针循环加载:push bc ld c,(hl) ;将频率加载到 BC inc hl ld b,(hl) inc hl ld e,(hl) ;将持续时间加载到 HL inc hl ld d,(hl) inc hl Push hl ;隐藏指针... ld h,b ; ...复制 BC 到 HL...ld l,c call $03b5 ; ...然后让 ROM 完成其余操作 pop hl pop bc djnz Loop ret scale: dw $066a,$0082,$05b3,$0092,$0511,$00a4,$04c6,$00ae dw $043d,$00c3,$03c4,$00dc,$0357,$00f6,$0325,$0105 这是一个“半波长”系统,就像我最初的 Apple II 声音播放例程一样,但其更精细的定时控制意味着音调的整体精度要好得多。基本上,我在第一篇文章中遇到的所有权衡都没有在这里出现,而且我们拥有我们期望从专门的巴黎圣日耳曼那里得到的那种控制力。老实说,这真的很棒。这里唯一真正的缺点是,需要像这样在双方都管理我们自己的部门有点尴尬;如果我们想要一个通用的例程来管理任意频率和持续时间的音调,我们需要带上自己的分频器。然而,正如我们很快就会看到的,在 16KB Spectrum 上我们没有任何其他选择,除了依靠这个例程之外我们别无选择。不过,我们有点超前了。在实际遇到 16K 系统上遇到的问题之前,让我们看看如何手动完成此操作。直接使用蜂鸣器 BIOS 例程已经足够好了,如果我要编写自己的振荡器函数,我希望它以完全不同的原理运行。幸运的是,我有这样一个可用的原理:基于频率而不是半波长的 16 位系统,这是我受 SID 和 Ensoniq 芯片内部结构的启发而创建的。由于 Z80 对 16 位数学的内置支持,它应该比 6502 代码更简单。基本思想是将我们的频率代码重复添加到正在运行的计数器中,并在每次计数器溢出时切换扬声器。棘手的部分实际上是确保我们不会弄乱其他任何东西,因为 I/O 端口 $FE 承担三重职责:低三位设置边框颜色。我们希望保留 BORDC 中的任何价值
这篇文章对您有帮助吗?

订阅66必读

每日精选科技资讯,直达你的邮箱