Game boy模拟器:输入
Posted 妇男主人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Game boy模拟器:输入相关的知识,希望对你有一定的参考价值。
通过在前五个部分开发的工作仿真器和界面,仿真系统能够运行基本的测试 ROM,并生成图形输出。模拟器目前无法做的是将按键作为键盘输入,并将它们馈送到被测 ROM;为此,必须模拟键盘对 I/O 寄存器的影响。
键盘
GameBoy 有一个单一的输入方法,一个八键小键盘,可以按下任意数量的键。对于大多数键盘,按键排列在列和行的网格中:这些可以被视为导线,键之间可以形成连接。当其中一列被激活时,连接到该列的任何行也将被激活,并且硬件能够检测活动行以确定当前按下的键。
在 GameBoy 中,键盘网格有两列四行,其优点是所有需要的连接都可以在一个 8 位 I/O 寄存器内完成。
键盘接线
由于所有六条线都与同一个寄存器相关联,因此 GameBoy 读取键盘的过程有点复杂:
- 将 0x10 或 0x20 写入 JOYP:这将激活列线之一的位 4 或 5;
- 等待几个周期让行连接传播到 JOYP;
- 检查 JOYP 的低四位,找出该列的活动行。
键盘的实现
编写代码来模拟键盘按压相对简单,但有两个因素使问题复杂化:允许在读取行之前在网格中设置一列,以及 javascript 使用的按键代码。为了容纳两列,仿真必须使用两个值,每个值都包含该列和行之间的交集。另一个需要考虑的因素是键盘的值是相反的:默认情况下,一行处于高电压状态,当它与一列相交时电压降至零。这被 I/O 寄存器解释为行位为 1 表示没有按键按下,0 表示按键按下。
JavaScript 的 keydown和keyup事件可用于确定按键何时被按下或释放;可以通过以下方式将这些绑定到键盘处理程序中。
key.js 源代码
KEY = {
_keys: [0x0F,0x0F],
_colidx: 0,
reset: function() {
KEY._keys = [0x0F,0x0F];
KEY._colidx = 0;
LOG.out('KEY', 'Reset.');
},
rb: function() {
switch(KEY._colidx)
{
case 0x00: return 0x00; break;
case 0x10: return KEY._keys[0]; break;
case 0x20: return KEY._keys[1]; break;
default: return 0x00; break;
}
},
wb: function(v) {
KEY._colidx = v&0x30;
},
keydown: function(e) {
switch(e.keyCode)
{
case 39: KEY._keys[1] &= 0xE; break;
case 37: KEY._keys[1] &= 0xD; break;
case 38: KEY._keys[1] &= 0xB; break;
case 40: KEY._keys[1] &= 0x7; break;
case 90: KEY._keys[0] &= 0xE; break;
case 88: KEY._keys[0] &= 0xD; break;
case 32: KEY._keys[0] &= 0xB; break;
case 13: KEY._keys[0] &= 0x7; break;
}
},
keyup: function(e) {
switch(e.keyCode)
{
case 39: KEY._keys[1] |= 0x1; break;
case 37: KEY._keys[1] |= 0x2; break;
case 38: KEY._keys[1] |= 0x4; break;
case 40: KEY._keys[1] |= 0x8; break;
case 90: KEY._keys[0] |= 0x1; break;
case 88: KEY._keys[0] |= 0x2; break;
case 32: KEY._keys[0] |= 0x5; break;
case 13: KEY._keys[0] |= 0x8; break;
}
}
};
key的功能的接入:
window.onkeydown = KEY.kdown;
window.onkeyup = KEY.kup;
除此之外,必须扩展 MMU 以处理键盘 I/O 寄存器,以及零页处理例程;下面给出了一个例子。
MMU.js:键盘输入/输出接口
rb: function(addr)
{
switch(addr & 0xF000)
{
...
case 0xF000:
switch(addr & 0x0F00)
{
...
// Zero-page
case 0xF00:
if(addr >= 0xFF80)
{
return MMU._zram[addr & 0x7F];
}
else if(addr >= 0xFF40)
{
// GPU (64 registers)
return GPU.rb(addr);
}
else switch(addr & 0x3F)
{
case 0x00: return KEY.rb();
default: return 0;
}
}
}
}
使用键盘处理程序后,剩下的问题是按键的处理,以及键盘代码区分被按下的不同键的能力。这可以通过 JavaScript 的event对象来完成;任何通过浏览器运行的事件,例如鼠标单击或按键,都将在被请求时传递给代码,以及描述刚刚发生的事件的对象。在按键的情况下,event对象包含一个字符代码和一个“键扫描”代码,它们都描述了相关的键。
通过Peter-Paul Koch 的测试,已经确定浏览器传递给 JavaScript 代码的字符代码是不可靠的,并且会根据使用的浏览器而变化。所有浏览器都同意的唯一情况是为keyup和keydown事件生成的键扫描代码;在任何浏览器中,按下一个给定的键将产生一个特定的值。
出于此仿真器的目的,键盘代码需要处理八个键:
扫描码 | 键盘 | 映射 |
---|---|---|
13 | Enter | Start |
32 | Space | Select |
37 | Left arrow | Left |
38 | Up arrow | Up |
39 | Right arrow | Right |
40 | Down arrow | Down |
88 | X | B |
90 | Z | A |
如上所述,当按键被按下时,相应的位必须被复位,当按键被释放时必须被置位。这可以如下实现。
按键动作
kdown: function(e)
{
switch(e.keyCode)
{
case 39: KEY._keys[1] &= 0xE; break;
case 37: KEY._keys[1] &= 0xD; break;
case 38: KEY._keys[1] &= 0xB; break;
case 40: KEY._keys[1] &= 0x7; break;
case 90: KEY._keys[0] &= 0xE; break;
case 88: KEY._keys[0] &= 0xD; break;
case 32: KEY._keys[0] &= 0xB; break;
case 13: KEY._keys[0] &= 0x7; break;
}
},
kup: function(e)
{
switch(e.keyCode)
{
case 39: KEY._keys[1] |= 0x1; break;
case 37: KEY._keys[1] |= 0x2; break;
case 38: KEY._keys[1] |= 0x4; break;
case 40: KEY._keys[1] |= 0x8; break;
case 90: KEY._keys[0] |= 0x1; break;
case 88: KEY._keys[0] |= 0x2; break;
case 32: KEY._keys[0] |= 0x4; break;
case 13: KEY._keys[0] |= 0x8; break;
}
}
以上是关于Game boy模拟器:输入的主要内容,如果未能解决你的问题,请参考以下文章