跳转到内容

超级任天堂编程/SPC700 参考资料

来自维基教科书,开放世界中的开放书籍

系统概述

[编辑 | 编辑源代码]
SPC700

SPC700 是索尼的协处理器,用于协调超级任天堂音频。一旦它被超级任天堂 CPU 发送的数据和代码初始化,它就会操作其附带的数字信号处理器 (DSP) 的状态,该处理器会产生输出音频。

SPC700 概述

[编辑 | 编辑源代码]

SPC700 拥有 64KB 的内存,用于存储代码和数据。在这个内存空间中,还包含内存映射寄存器,用于与超级任天堂 CPU、DSP 和三个可用的定时器进行通信。

SPC700 有 6 个寄存器:

  • A - 一个 8 位累加器
  • X & Y - 8 位索引寄存器
  • SP - 8 位堆栈指针
  • PC - 16 位程序计数器
  • PSW - 8 位“程序状态字”,用于存储状态标志

Y 和 A 寄存器可以组合成一个 16 位寄存器,其中 Y 是高字节,用于某些操作。

DSP 概述

[编辑 | 编辑源代码]

DSP 有 8 个通道,每个通道可以播放 16 位声音。所有 8 个通道都具有独立的左右立体声音量,可以在不同的音高下播放,并且可以应用攻击-衰减-持续-释放 (ADSR) 包络。可以将一个白噪声源设置为替换任何 8 个通道上的采样数据。此外,DSP 可以对音频应用回声。16 位音频样本从 SPC700 的 64KB 内存空间中读取,它们以压缩的 4 位有损格式存储。

SPC700 内存映射

[编辑 | 编辑源代码]
  • $0000 - $00EF - 直接页 0
  • $00F0 - $00FF - 内存映射硬件寄存器
  • $0100 - $01FF - 直接页 1
  • $0100 - $01FF - 潜在的堆栈内存
  • $FFC0 - $FFFF - IPL ROM

直接页寻址

[编辑 | 编辑源代码]

许多指令提供一种寻址模式,用于访问“直接页”中的 1 字节内存地址。这种寻址模式会生成更短的字节码,这可能导致执行速度更快。直接页的高字节是 $00$01,对应于 PSW 寄存器中的 P 位。

堆栈指针的低字节由 SP 寄存器指定;高字节始终是 $01。堆栈指针在 IPL ROM 重启时设置为 $01EF,并向下增长。

重启时,64KB RAM 末尾的 64 字节内存块被初始化为 IPL ROM 的内容,这是执行开始的地方。IPL ROM 中的代码将堆栈指针设置为 $01EF,将内存从 $0000 清零到 $00EF,然后等待来自超级任天堂的通过输入端口的数据。

SPC700 寄存器

[编辑 | 编辑源代码]
地址 描述 读/写
$00F0 未知 ?
$00F1 控制
$00F2 DSP 读/写地址 读/写
$00F3 DSP 读/写数据 读/写
$00F4 端口 0 读/写
$00F5 端口 1 读/写
$00F6 端口 2 读/写
$00F7 端口 3 读/写
$00FA 定时器设置 0
$00FB 定时器设置 1
$00FC 定时器设置 2
$00FA 定时器计数器 0
$00FB 定时器计数器 1
$00FC 定时器计数器 2

$00F0 未知

[编辑 | 编辑源代码]

Anomie 对此寄存器进行了一些测试。romhacking.net 上的一份文档对此进行了描述。

$00F1 控制

[编辑 | 编辑源代码]
7 6 5 4 3 2 1 0
X X PC32 PC10 X ST2 ST1 ST0
  • PC32 - 在此位写入 1 将使端口 2 和 3 的输入归零
  • PC10 - 在此位写入 1 将使端口 1 和 0 的输入归零
  • ST0-2 - 这些用于启动定时器。
 Warning: Writing to this register will always restart/stop all of the timers.

$00F2/$00F3 DSP 寄存器

[编辑 | 编辑源代码]

写入 $00F2 会设置要访问的 DSP 寄存器的地址。写入 $00F3 会更改指向的寄存器的值。从 $00F3 读取将返回指向的寄存器的值。允许向 $00F2 写入一个字,它可以用于同时设置地址和向寄存器写入一个值。

$00F4-$00F7 端口

[编辑 | 编辑源代码]

从这些端口读取将为您提供超级任天堂在 $2140-$2143 设置的值。写入这些寄存器的值可以通过超级任天堂使用相同的 $2140-$2143 查看。可以使用控制寄存器清除这些端口的输入。

$00FA-$00FF 定时器设置

[编辑 | 编辑源代码]

寄存器 $00FA-$00FC 用于设置定时器速率。定时器 0 和 1 的分辨率为 125 微秒。定时器 2 的分辨率更精细,约为 15.6 微秒。$00FD-$00FF 是包含定时器溢出计数的 4 位寄存器。以下是定时器的工作方式。

每个定时器都有一个内部计数器,它在每个时钟输入时递增。如果它等于 $00FA-$00FC 中的数字(取决于您使用的是哪个定时器),相应的计数器寄存器将递增,内部计数器将复位。计数器寄存器 ($00FD-$00FF) 是 4 位寄存器,只能读取。从它们读取会导致它们复位为零。如果您没有在有限的时间范围内读取计数器,它们也会溢出并被清除为零。必须在设置 $00FA-$00FC 寄存器之前停止定时器。要启动定时器,请写入控制寄存器位 0-2。要停止定时器,请复位这些位。请注意,写入控制寄存器将重新启动现有的定时器。

DSP 压缩格式

[编辑 | 编辑源代码]

DSP 使用一种特殊的 ADPCM 编码声音格式。样本由一系列 9 字节的压缩块组成。每个块包含 16 个 4 位样本和一个 1 字节的头部。16 位样本将获得 9/32 的压缩比,但 8 位样本必须在压缩之前膨胀为 16 位(使其仅获得 9/16 的压缩比)。每个块的第一个字节包含头部。

7 6 5 4 3 2 1 0
范围 滤波器 循环 结束
  • 范围 - 这是数据的移位值。它可以是 0-12。
  • 滤波器 - 这选择解码过程中使用的滤波器系数。(见下表)
  • 循环 - 此位将块标记为循环内的块。此位的确切功能未知,但我检查过的某些商业 SPC 样本将此位设置为样本中的所有块。
  • 结束 - 此位将块标记为样本中的最后一个块。如果 DSP 通道到达此块,它将终止或跳转到循环点。

每个块包含 8 字节的样本数据(16 个带符号的 4 位样本)。每个字节中的较高 nibble 包含在较低 nibble 中解码的样本之前要解码的样本。

解码过程

[编辑 | 编辑源代码]

以下公式用于估计 DSP 的 16 位输出。设 y 和 z 为最后两个先前解码的样本。'a' 和 'b' 是由头部字节的第 2-3 位选择的滤波器系数。

 sample = S + ay + bz
 S is the shifted data:
 S = (4-bit sample) << Range

DSP 使用最少的移位操作执行此过程;输出精度不会很完美。它还将高斯插值应用于输出。

滤波器系数

[编辑 | 编辑源代码]
滤波器# a b
0 0 0
1 15/16 0
2 61/32 -15/16
3 115/64 -13/16

进一步阅读

[编辑 | 编辑源代码]
华夏公益教科书