Loading

从零开始的nes模拟器-[6] 控制设备

参考:

官方手柄布局

NES 支持许多设备,最常见的还是官方手柄,它有 8 个按键:

image-20220207122655744

对应的寄存器

手柄的寄存器位于 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];
posted @ 2022-02-15 15:19  CHZarles  阅读(369)  评论(0)    收藏  举报