内存记忆体

  • 详细地查看内存区域
    • [0000-3FFF] 卡带程序
      • [0000-00FF] BIOS
      • [0100-014F] 卡带头部
    • [4000-7FFF] 卡带程序(其他)
    • [8000-9FFF] 显存
    • [A000-BFFF] 卡带(拓展)RAM
    • [C000-DFFF] 运行内存RAM
    • [E000-FDFF] 运行内存RAM(映射)
    • [FE00-FE9F] 图形:角色信息
    • [FF00-FF7F] 内存 I/O 映射
    • [FF80-FFFF] 零页 RAM
  • 与 CPU 的接口
  • 加载 ROM
  • 下一步

在本系列的前一部分中,计算机被介绍为一个处理单元,它从内存中获取指令。几乎在所有情况下,计算机的内存都不是简单的连续区域; GameBoy 在这方面也不例外。由于 GameBoy CPU 可以访问其地址总线上的 65,536 个单独位置,因此可以绘制 CPU 可以访问的所有区域的“内存映射”。

详细地查看内存区域

[0000-3FFF] 卡带程序

卡带程序的前 16,384 个字节在内存映射中始终可用。特殊情况适用:

[0000-00FF] BIOS

当 CPU 启动时,PC 从 0000h 开始,这是 256 字节 GameBoy BIOS 代码的开始。一旦 BIOS 运行,它就会从内存映射中删除,并且盒式 ROM 的这个区域变得可寻址。

[0100-014F] 卡带头部

这一部分包含有关其名称和制造商的数据,并且必须以特定格式写入。

[4000-7FFF] 卡带程序(其他)

任何后续的16k 的盒式程序都可以在这里 一一 提供给CPU;卡带上的芯片通常用于在段之间切换,并使特定区域可访问。最小的程序是32k,这意味着不需要选段芯片。

[8000-9FFF] 显存

图形子系统使用的背景和角色所需的数据保存在这里,并且可以通过卡带程序进行更改。

[A000-BFFF] 卡带(拓展)RAM

GameBoy 中有少量可写内存;如果制作的游戏需要的 RAM 多于硬件中可用的 RAM,则可以在此处设置额外的 8k 块 RAM 可寻址。

[C000-DFFF] 运行内存RAM

GameBoy 的内部 8k RAM,可由 CPU 读取或写入。

[E000-FDFF] 运行内存RAM(映射)

由于 GameBoy 硬件的接线,工作 RAM 的精确副本可在内存映射中高出 8k。此副本在映射的最后 512 字节之前可用,其他区域可在此处访问。

[FE00-FE9F] 图形:角色信息

图形芯片渲染的角色数据保存在这里,包括精灵的位置和属性。

[FF00-FF7F] 内存 I/O 映射

GameBoy 的每个子系统(图形、声音等)都有控制值,以允许程序创建效果和使用硬件。这些值可直接在该区域的地址总线上提供给 CPU。

[FF80-FFFF] 零页 RAM

内存顶部有一个 128 字节的高速 RAM 区域。虽然这是内存的“页面”255,但它被称为第 0 页,因为程序和 GameBoy 硬件之间的大部分交互都是通过使用这页内存发生的。

与 CPU 的接口

为了让仿真 CPU 分别访问这些区域,每个区域都必须在内存管理单元中作为特殊情况处理。这部分代码在前面的部分已经提到过,是为MMU对象描述的一个基本接口;接口的完善可以像 switch 语句一样简单。

文件:MMU.js

MMU = {_bios: [0x31, 0xFE, 0xFF, 0xAF, 0x21, 0xFF, 0x9F, 0x32, 0xCB, 0x7C, 0x20, 0xFB, 0x21, 0x26, 0xFF, 0x0E,0x11, 0x3E, 0x80, 0x32, 0xE2, 0x0C, 0x3E, 0xF3, 0xE2, 0x32, 0x3E, 0x77, 0x77, 0x3E, 0xFC, 0xE0,0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B,0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9,0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20,0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04,0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2,0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06,0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xF2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20,0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17,0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3c, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x4C,0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20,0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50],_rom: '',_carttype: 0,_mbc: [{},{rombank:0, rambank:0, ramon:0, mode:0}],_romoffs: 0x4000,_ramoffs: 0,_eram: [],_wram: [],_zram: [],_inbios: 1,_ie: 0,_if: 0,reset: function() {for(i=0; i<8192; i++) MMU._wram[i] = 0;for(i=0; i<32768; i++) MMU._eram[i] = 0;for(i=0; i<127; i++) MMU._zram[i] = 0;MMU._inbios=1;MMU._ie=0;MMU._if=0;MMU._carttype=0;MMU._mbc[0] = {};MMU._mbc[1] = {rombank:0, rambank:0, ramon:0, mode:0};MMU._romoffs=0x4000;MMU._ramoffs=0;LOG.out('MMU', 'Reset.');},load: function(file) {b=new BinFileReader(file);MMU._rom=b.readString(b.getFileSize(), 0);MMU._carttype = MMU._rom.charCodeAt(0x0147);LOG.out('MMU', 'ROM loaded, '+MMU._rom.length+' bytes.');},rb: function(addr) {switch(addr&0xF000){// ROM bank 0case 0x0000:if(MMU._inbios){if(addr<0x0100) return MMU._bios[addr];else if(Z80._r.pc == 0x0100){MMU._inbios = 0;LOG.out('MMU', 'Leaving BIOS.');}}else{return MMU._rom.charCodeAt(addr);}case 0x1000:case 0x2000:case 0x3000:return MMU._rom.charCodeAt(addr);// ROM bank 1case 0x4000: case 0x5000: case 0x6000: case 0x7000:return MMU._rom.charCodeAt(MMU._romoffs+(addr&0x3FFF));// VRAMcase 0x8000: case 0x9000:return GPU._vram[addr&0x1FFF];// External RAMcase 0xA000: case 0xB000:return MMU._eram[MMU._ramoffs+(addr&0x1FFF)];// Work RAM and echocase 0xC000: case 0xD000: case 0xE000:return MMU._wram[addr&0x1FFF];// Everything elsecase 0xF000:switch(addr&0x0F00){// Echo RAMcase 0x000: case 0x100: case 0x200: case 0x300:case 0x400: case 0x500: case 0x600: case 0x700:case 0x800: case 0x900: case 0xA00: case 0xB00:case 0xC00: case 0xD00:return MMU._wram[addr&0x1FFF];// OAMcase 0xE00:return ((addr&0xFF)<0xA0) ? GPU._oam[addr&0xFF] : 0;// Zeropage RAM, I/O, interruptscase 0xF00:if(addr == 0xFFFF) { return MMU._ie; }else if(addr > 0xFF7F) { return MMU._zram[addr&0x7F]; }else switch(addr&0xF0){case 0x00:switch(addr&0xF){case 0: return KEY.rb();    // JOYPcase 4: case 5: case 6: case 7:return TIMER.rb(addr);case 15: return MMU._if;    // Interrupt flagsdefault: return 0;}case 0x10: case 0x20: case 0x30:return 0;case 0x40: case 0x50: case 0x60: case 0x70:return GPU.rb(addr);}}}},rw: function(addr) { return MMU.rb(addr)+(MMU.rb(addr+1)<<8); },wb: function(addr,val) {switch(addr&0xF000){// ROM bank 0// MBC1: Turn external RAM oncase 0x0000: case 0x1000:switch(MMU._carttype){case 1:MMU._mbc[1].ramon = ((val&0xF)==0xA)?1:0;break;}break;// MBC1: ROM bank switchcase 0x2000: case 0x3000:switch(MMU._carttype){case 1:MMU._mbc[1].rombank &= 0x60;val &= 0x1F;if(!val) val=1;MMU._mbc[1].rombank |= val;MMU._romoffs = MMU._mbc[1].rombank * 0x4000;break;}break;// ROM bank 1// MBC1: RAM bank switchcase 0x4000: case 0x5000:switch(MMU._carttype){case 1:if(MMU._mbc[1].mode){MMU._mbc[1].rambank = (val&3);MMU._ramoffs = MMU._mbc[1].rambank * 0x2000;}else{MMU._mbc[1].rombank &= 0x1F;MMU._mbc[1].rombank |= ((val&3)<<5);MMU._romoffs = MMU._mbc[1].rombank * 0x4000;}}break;case 0x6000: case 0x7000:switch(MMU._carttype){case 1:MMU._mbc[1].mode = val&1;break;}break;// VRAMcase 0x8000: case 0x9000:GPU._vram[addr&0x1FFF] = val;GPU.updatetile(addr&0x1FFF, val);break;// External RAMcase 0xA000: case 0xB000:MMU._eram[MMU._ramoffs+(addr&0x1FFF)] = val;break;// Work RAM and echocase 0xC000: case 0xD000: case 0xE000:MMU._wram[addr&0x1FFF] = val;break;// Everything elsecase 0xF000:switch(addr&0x0F00){// Echo RAMcase 0x000: case 0x100: case 0x200: case 0x300:case 0x400: case 0x500: case 0x600: case 0x700:case 0x800: case 0x900: case 0xA00: case 0xB00:case 0xC00: case 0xD00:MMU._wram[addr&0x1FFF] = val;break;// OAMcase 0xE00:if((addr&0xFF)<0xA0) GPU._oam[addr&0xFF] = val;GPU.updateoam(addr,val);break;// Zeropage RAM, I/O, interruptscase 0xF00:if(addr == 0xFFFF) { MMU._ie = val; }else if(addr > 0xFF7F) { MMU._zram[addr&0x7F]=val; }else switch(addr&0xF0){case 0x00:switch(addr&0xF){case 0: KEY.wb(val); break;case 4: case 5: case 6: case 7: TIMER.wb(addr, val); break;case 15: MMU._if = val; break;}break;case 0x10: case 0x20: case 0x30:break;case 0x40: case 0x50: case 0x60: case 0x70:GPU.wb(addr,val);break;}}break;}},ww: function(addr,val) { MMU.wb(addr,val&255); MMU.wb(addr+1,val>>8); }
};

在上面的代码段中,需要注意的是,0xFF00 和 0xFF7F 之间的内存区域是未处理的;这些位置用作提供 I/O 的各种芯片的内存映射 I/O,并将在后面的部分中介绍这些系统时对其进行定义。写入字节的处理方式非常相似;每个操作都是相反的,值被写入内存的各个区域,而不是从函数返回。

加载 ROM

正如 CPU 仿真没有其内存访问、图形等支持元素就毫无用处一样,如果没有加载程序,能够从内存中读取程序也毫无用处。将程序拉入模拟器有两种主要方法:将其硬编码到模拟器的源代码中,或允许从某个位置加载 ROM 文件。硬编码程序的明显缺点是它是固定的,不能轻易更改。

对于这个 JavaScript 模拟器,GameBoy BIOS 被硬编码到 MMU 中,因为它不容易改变;然而,在模拟器初始化之后,程序文件是从服务器异步加载的。这可以通过 XMLHTTP 完成,使用二进制文件阅读器,例如 Andy Na 的 BinFileReader;其结果是一个包含 ROM 文件的字符串。

内存加载ROM可以看上面的 load 方法,里面的 BinFileReader 来自于下面的 fileread.js 源码

文件:fileread.js

/*** BinFileReader.js* You can find more about this function at* http://nagoon97.com/reading-binary-files-using-ajax/** Copyright (c) 2008 Andy G.P. Na <nagoon97@naver.com>* The source code is freely distributable under the terms of an MIT-style license.*/
function BinFileReader(fileURL){var _exception = {};_exception.FileLoadFailed = 1;_exception.EOFReached = 2;var filePointer = 0;var fileSize = -1;var fileContents;this.getFileSize = function(){return fileSize;}this.getFilePointer = function(){return filePointer;}this.movePointerTo = function(iTo){if(iTo < 0) filePointer = 0;else if(iTo > this.getFileSize()) throwException(_exception.EOFReached);else filePointer = iTo;return filePointer;};this.movePointer = function(iDirection){this.movePointerTo(filePointer + iDirection);return filePointer;};this.readNumber = function(iNumBytes, iFrom){iNumBytes = iNumBytes || 1;iFrom = iFrom || filePointer;this.movePointerTo(iFrom + iNumBytes);var result = 0;for(var i=iFrom + iNumBytes; i>iFrom; i--){result = result * 256 + this.readByteAt(i-1);}return result;};this.readString = function(iNumChars, iFrom){iNumChars = iNumChars || 1;iFrom = iFrom || filePointer;this.movePointerTo(iFrom);var result = "";var tmpTo = iFrom + iNumChars;for(var i=iFrom; i<tmpTo; i++){result += String.fromCharCode(this.readNumber(1));}return result;};this.readUnicodeString = function(iNumChars, iFrom){iNumChars = iNumChars || 1;iFrom = iFrom || filePointer;this.movePointerTo(iFrom);var result = "";var tmpTo = iFrom + iNumChars*2;for(var i=iFrom; i<tmpTo; i+=2){result += String.fromCharCode(this.readNumber(2));}return result;};function throwException(errorCode){switch(errorCode){case _exception.FileLoadFailed:throw new Error('Error: Filed to load "'+fileURL+'"');break;case _exception.EOFReached:throw new Error("Error: EOF reached");break;}}function BinFileReaderImpl_IE(fileURL){var vbArr = BinFileReaderImpl_IE_VBAjaxLoader(fileURL);fileContents = vbArr.toArray();fileSize = fileContents.length-1;if(fileSize < 0) throwException(_exception.FileLoadFailed);this.readByteAt = function(i){return fileContents[i];}}function BinFileReaderImpl(fileURL){var req = new XMLHttpRequest();req.open('GET', fileURL, false);//XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com] req.overrideMimeType('text/plain; charset=x-user-defined');req.send(null);if (req.status != 200) throwException(_exception.FileLoadFailed);fileContents = req.responseText;fileSize = fileContents.length;this.readByteAt = function(i){return fileContents.charCodeAt(i) & 0xff;}}if(/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent))BinFileReaderImpl_IE.apply(this, [fileURL]);elseBinFileReaderImpl.apply(this, [fileURL]);
}document.write('<script type="text/vbscript">\n\
Function BinFileReaderImpl_IE_VBAjaxLoader(fileName)\n\Dim xhr\n\Set xhr = CreateObject("Microsoft.XMLHTTP")\n\
\n\xhr.Open "GET", fileName, False\n\
\n\xhr.setRequestHeader "Accept-Charset", "x-user-defined"\n\xhr.send\n\
\n\Dim byteArray()\n\
\n\if xhr.Status = 200 Then\n\Dim byteString\n\Dim i\n\
\n\byteString=xhr.responseBody\n\
\n\ReDim byteArray(LenB(byteString))\n\
\n\For i = 1 To LenB(byteString)\n\byteArray(i-1) = AscB(MidB(byteString, i, 1))\n\Next\n\End If\n\
\n\BinFileReaderImpl_IE_VBAjaxLoader=byteArray\n\
End Function\n\
</script>');

由于 ROM 文件保存为字符串,而不是数字数组,因此必须更改 rb 和 wb 函数以索引字符串:

ROM 文件索引

case 0x1000:
case 0x2000:
case 0x3000:return MMU._rom.charCodeAt(addr);

下一步

有了 CPU 和 MMU,就可以逐步观察正在执行的程序:可以实现仿真,并在正确的寄存器中生成预期值。缺少的是对图形输出意味着什么的感觉。在本系列的下一部分中,将研究图形问题,包括 GameBoy 如何构建其图形输出,以及如何将图形渲染到屏幕上。

Game boy模拟器(2):运行内存相关推荐

  1. Android内核的编译和调试

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/70500488 一.Android内核源码的选择 Android手机设备内核源码的调 ...

  2. 支付宝现异地登录 回应称系外部网站遭泄露 Droid4X-win amd cpu 只支持部分

    支付宝现异地登录 回应称系外部网站遭泄露 2015-10-01 16:18:17 来源: 每日经济新闻 分享到: 0 如今,移动支付已经成为了许多消费者购物或旅游的重要支付方式.支付宝.微信支付等工具 ...

  3. Appium之创建、连接夜神模拟器

    或许当前有些小伙伴没 android 手机,这时候可以在电脑上开个模拟器玩玩,下面就分别介绍两种不同的模拟器,下面和大家一起学习交流. 一.android 模拟器 AVD 模拟器配置 1.双击启动 A ...

  4. Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅

    原文:Android零基础入门第7节:搞定Android模拟器,开启甜蜜之旅 在前几期中总结分享了Android的前世今生.Android 系统架构和应用组件那些事.带你一起来聊一聊Android开发 ...

  5. 怎么增加android模拟器内存卡,增加android模拟器的内存大小

    Android模拟器启动速度慢是众所周知的,运行也很缓慢如何加速你的Android Emulator呢? 今天Android123给大家支招了,修改Android模拟器RAM大小,运行流畅度加倍,尤其 ...

  6. pc端无法ping android模拟器_【内附下载方式】PC端最新宝可梦 Lets Go去皮去伊模拟器+最新dlc+mod...

    游戏名称:精灵宝可梦 Let's go 皮卡丘 +伊布 游戏总大小:9GB+ 游戏简介: <精灵宝可梦 Lets Go 皮卡丘/伊布>是宝可梦系列全新作品,也是该系列首次登上Switch主 ...

  7. android+模拟器+ram,Android模拟器RAM修改方法 - 尤其是3.0

    我们以Windows平台的SDK为例,这里Android开发网的模拟器配置路径为 C:/Documents and Settings/android/.android/avd/android3.avd ...

  8. 永远的7日之都怎么在电脑上玩 永远的7日之都安卓模拟器教程

    <永远的7日之都>是一款网易自研的都市幻想题材的RPG,采用了标准的都市灾难题材,人类的世界出现了"黑门",大量异种入侵,人类经过努力将黑门控制在了主角所在的城市,主角 ...

  9. 猎人X猎人手游如何在电脑上玩 猎人X猎人手游模拟器教程

    <猎人×猎人>是一款大型多人在线的角色扮演动作手游,在独特的角色培养基础上,忠实原著,极致剧情还原,可以说算是圆了曾经的梦,虽然这个梦来得有点晚.接下来,和小编一起看下猎人X猎人手游模拟器 ...

最新文章

  1. 2021 年中国敏捷行业现状调查全面启动
  2. 输入、输出与Mad Libs游戏
  3. latex转word_Latex与word转换及相关问题解决方法
  4. 判断包含字符当中包含小数点_Python|提取包含指定文本的行到一个文本文件(字符串或正则)...
  5. OpenGL.tutorial06键盘和鼠标
  6. 【网络】半双工和全双工
  7. 如何在装有 macOS Monterey 的 Mac 上使用省电模式?
  8. 2月第4周网络安全报告:境内69.1万主机感染病毒
  9. 谈谈滞后补偿器与PI控制及其原理分析
  10. Python 中文数字对照表 输入一个数字,转换成中文数字。比如:1234567890 -> 壹贰叁肆伍陆柒捌玖零。【简单易懂,代码可以直接运行】
  11. 草根创业,我劝你抓住网络培训的机会!
  12. 区块链 交易和区块数据存在哪儿
  13. 谈谈本人做广告联盟的经验
  14. 收入为什么增加记入贷方而不是借方--完美解答
  15. 什么是 知足者常乐?
  16. 拿去吧!27款MacBook软件推荐给你
  17. 【日常折腾】Y7000P触控板失灵这件事
  18. 在 Jupyter Notebook 中使用R语言
  19. Chrome 技术篇-未安装的crx插件源码查看,crx类型文件解压方法
  20. PMP中的那些进度/成本图

热门文章

  1. java计算机毕业设计营养分析系统源码+数据库+系统+lw文档+部署
  2. 画板格式化记录(优化布局)
  3. Generalizing A Person Retrieval Model Hetero- and Homogeneously
  4. 程序员的《致女儿书》
  5. 谷歌浏览器如何一键清除所有断点
  6. 视频会议必备设备-无线流媒体网关
  7. python工程师需要什么技能_江门北大青鸟:Python工程师都会哪些技能?
  8. [原]利用ps制作金属纹理效果
  9. 5. SpringSecurity用户认证源码 与 实现短信验证码(自定义SpringSecurity组件)
  10. MInesweeper