从零开始的nes模拟器-[6] 控制设备
参考:
官方手柄布局
NES 支持许多设备,最常见的还是官方手柄,它有 8 个按键:

对应的寄存器
手柄的寄存器位于 CPU 总线的 0x4016 和 0x4017,分别对应 1P 和 2P
7 bit 0
---- ----
xxxx xxxS
|
+- Controller shift register strobe
寄存器只有 bit 0 有效,做为选通标志。当写入选通为 1 时,则可以通过读取寄存器,每次返回一个按键状态,下一次读取返回下一个按钮状态。( 所以要读8次,返回顺序是从 A 到 Right )
按钮对应的比特位为 :
| bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| button | A | B | Select | Start | Up | Down | Left | Right |
当写入 0 时,offset 被置位,再次选通读取时会重新从第一个按钮读取
需要注意的是,只能往 4016 写(写 4017 给 APU 用了),读可以往 4016 和 4017 读。写 4016 时,对两个手柄都有效,读时则 4016 为 P1,4017 为 P2
代码实现
controler接口
// 设备状态 Internal cache of controller state
// 这是内部使用的变量
// 0x4016 对应 idx 为 0
// 0x4017 对应 idx 为 1
uint8_t controller_state[2];
// 控制类外设 Controllers
// 这是面向外设的接口
// 0x4016 对应 idx 为 0
// 0x4017 对应 idx 为 1
// 各种状态会随输入按按键状态而改变
uint8_t controller[2];
记录状态
//每渲染一帧游戏都要接收输入情况
// Sneaky peek of controller input in next video! ;P
nes.controller[0] = 0x00;
nes.controller[0] |= GetKey(olc::Key::X).bHeld ? 0x80 : 0x00;
nes.controller[0] |= GetKey(olc::Key::Z).bHeld ? 0x40 : 0x00;
nes.controller[0] |= GetKey(olc::Key::A).bHeld ? 0x20 : 0x00;
nes.controller[0] |= GetKey(olc::Key::S).bHeld ? 0x10 : 0x00;
nes.controller[0] |= GetKey(olc::Key::UP).bHeld ? 0x08 : 0x00;
nes.controller[0] |= GetKey(olc::Key::DOWN).bHeld ? 0x04 : 0x00;
nes.controller[0] |= GetKey(olc::Key::LEFT).bHeld ? 0x02 : 0x00;
nes.controller[0] |= GetKey(olc::Key::RIGHT).bHeld ? 0x01 : 0x00;
读操作
data = (controller_state[addr & 0x0001] & 0x80) > 0;
controller_state[addr & 0x0001] <<= 1;
写操作
// 直接赋值就好
controller_state[addr & 0x0001] = controller[addr & 0x0001];

浙公网安备 33010602011771号