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事件生成的键扫描代码;在任何浏览器中,按下一个给定的键将产生一个特定的值。

出于此仿真器的目的,键盘代码需要处理八个键:

扫描码键盘映射
13EnterStart
32SpaceSelect
37Left arrowLeft
38Up arrowUp
39Right arrowRight
40Down arrowDown
88XB
90ZA

如上所述,当按键被按下时,相应的位必须被复位,当按键被释放时必须被置位。这可以如下实现。

按键动作

    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模拟器:输入的主要内容,如果未能解决你的问题,请参考以下文章

Game boy模拟器:运行内存

Game boy模拟器:图形

Game boy模拟器:内存池

Game boy模拟器:完整的 Z80 内核CPU源码

Game boy模拟器:CPU

Game boy模拟器:中断