超级任天堂编程/SPC700 参考资料
SPC700 是索尼的协处理器,用于协调超级任天堂音频。一旦它被超级任天堂 CPU 发送的数据和代码初始化,它就会操作其附带的数字信号处理器 (DSP) 的状态,该处理器会产生输出音频。
SPC700 拥有 64KB 的内存,用于存储代码和数据。在这个内存空间中,还包含内存映射寄存器,用于与超级任天堂 CPU、DSP 和三个可用的定时器进行通信。
SPC700 有 6 个寄存器:
- A - 一个 8 位累加器
- X & Y - 8 位索引寄存器
- SP - 8 位堆栈指针
- PC - 16 位程序计数器
- PSW - 8 位“程序状态字”,用于存储状态标志
Y 和 A 寄存器可以组合成一个 16 位寄存器,其中 Y 是高字节,用于某些操作。
DSP 有 8 个通道,每个通道可以播放 16 位声音。所有 8 个通道都具有独立的左右立体声音量,可以在不同的音高下播放,并且可以应用攻击-衰减-持续-释放 (ADSR) 包络。可以将一个白噪声源设置为替换任何 8 个通道上的采样数据。此外,DSP 可以对音频应用回声。16 位音频样本从 SPC700 的 64KB 内存空间中读取,它们以压缩的 4 位有损格式存储。
$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
,然后等待来自超级任天堂的通过输入端口的数据。
地址 | 描述 | 读/写 |
---|---|---|
$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 | 读 |
Anomie 对此寄存器进行了一些测试。romhacking.net 上的一份文档对此进行了描述。
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
会设置要访问的 DSP 寄存器的地址。写入 $00F3
会更改指向的寄存器的值。从 $00F3
读取将返回指向的寄存器的值。允许向 $00F2
写入一个字,它可以用于同时设置地址和向寄存器写入一个值。
从这些端口读取将为您提供超级任天堂在 $2140
-$2143
设置的值。写入这些寄存器的值可以通过超级任天堂使用相同的 $2140
-$2143
查看。可以使用控制寄存器清除这些端口的输入。
寄存器 $00FA
-$00FC
用于设置定时器速率。定时器 0 和 1 的分辨率为 125 微秒。定时器 2 的分辨率更精细,约为 15.6 微秒。$00FD
-$00FF
是包含定时器溢出计数的 4 位寄存器。以下是定时器的工作方式。
每个定时器都有一个内部计数器,它在每个时钟输入时递增。如果它等于 $00FA
-$00FC
中的数字(取决于您使用的是哪个定时器),相应的计数器寄存器将递增,内部计数器将复位。计数器寄存器 ($00FD
-$00FF
) 是 4 位寄存器,只能读取。从它们读取会导致它们复位为零。如果您没有在有限的时间范围内读取计数器,它们也会溢出并被清除为零。必须在设置 $00FA
-$00FC
寄存器之前停止定时器。要启动定时器,请写入控制寄存器位 0-2。要停止定时器,请复位这些位。请注意,写入控制寄存器将重新启动现有的定时器。
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 |