内存池

  • 内存池与延展
  • MBCs and the cartridge header
  • MBC1的实现
  • 接下来

到目前为止,在本系列中,我们一直在处理GameBoy的简单内存映射的加载和仿真,整个游戏 ROM 都适合内存的下半部分。完全适合内存的游戏并不多(俄罗斯方块是少数游戏之一);大多数游戏都比这大,并且必须采用独立机制将游戏 ROM 的“银行”交换到 GameBoy CPU 的视图中。

GameBoy 库中的一些第一批游戏是在卡带内使用内存库控制器构建的,它完成了将 ROM 库交换到视图中的工作;一直以来,各种版本的卡带 MBC 都是为越来越大的游戏而设计的。在与此部分相关的演示的特定示例中,MBC 的第一个版本用于处理 64kB ROM 的加载。

内存池与延展

多年来,许多计算机系统不得不处理程序过多而无法装入内存的问题。传统上,有两种方法可以解决这个问题。

  • 增加地址空间:构建具有更多地址线的新 CPU,使其能够查看和理解更大的内存量。这是首选的解决方案,但需要大量时间来重新开发有问题的计算机系统,并且可能需要对 CPU 的支持芯片组进行更多更改。
  • 虚拟内存:这可以指在磁盘上保存大块 RAM,并在需要时进行交换;或者在需要时交换预先写入的 ROM 块。在这两种情况下,系统硬件几乎不需要扩展,但系统的任何软件都必须知道寻呼/银行系统,以便使用它。

由于GameBoy是一个固定的硬件平台,分布广泛,在制作较大的游戏时没有办法增加地址空间;取而代之的是,内置在卡带中的内存库控制器提供了一种将 16kB ROM 库切换到视图中的方法。除此之外,MBC1 支持高达 32kB 的“外部 RAM”,这是盒式存储器中的可写存储器;[A000-BFFF]如果可用,这可以存入内存映射中的空间。

为了方便软件使用MBC1,ROM的第一个16kB bank(bank 0)被固定在地址处0000;ROM 空间的后半部分可以在 1 到 127 之间的任何 ROM 组上制作一个窗口,最大 ROM 大小为 2048kB。MBC1 的一个奇怪之处在于它在 32 位内部进行交易:银行 #32、#64 和 #96 无法访问,因为它们在银行系统内被视为银行 #0。这意味着除了固定 bank #0 之外还有 125 个 bank 可用。

MBC1 芯片中有四个寄存器,可以切换 ROM 和 RAM 的存储体;这些可以通过写入特定范围内的(通常是只读的)ROM 空间来更改。详细信息如下表所示。

MBC1 寄存器组

Locations Register Details
0000-1FFF Enable external RAM 4 bits wide; value of
0x0A enables RAM,
any other value disables
2000-3FFF ROM bank (low 5 bits) Switch between banks 1-31 (value 0 is seen as 1)
4000-5FFF ROM bank (high 2 bits)
RAM bank
ROM mode: switch ROM bank "set" {1-31}-{97-127}
RAM mode: switch RAM bank 0-3
6000-7FFF Mode 0: ROM mode (no RAM banks, up to 2MB ROM)
1: RAM mode (4 RAM banks, up to 512kB ROM)

MBCs and the cartridge header

由于有多种用于内存池业务的控制器,任何给定的游戏都必须在卡带头数据中说明使用的是哪个 MBC。这是盒式 ROM 中的第一个数据块,并遵循特定格式。

Cartridge header format

Location(s) Value Size
(bytes)
Details
0100-0103h Entry point 4 Where the game starts
Usually "NOP; JP 0150h"
0104-0133h Nintendo logo 48 Used by the BIOS to verify checksum
0134-0143h Title 16 Uppercase, padded with 0
0144-0145h Publisher 2 Used by newer GameBoy games
0146h Super GameBoy flag 1 Value of 3 indicates SGB support
0147h Cartridge type 1 MBC type/extras
0148h ROM size 1 Usually between 0 and 7
Size = 32kB << [0148h]
0149h RAM size 1 Size of external RAM
014Ah Destination 1 0 for Japan market, 1 otherwise
014Bh Publisher 1 Used by older GameBoy games
014Ch ROM version 1 Version of the game, usually 0
014Dh Header checksum 1 Checked by BIOS before loading
014E-014Fh Global checksum 2 Simple summation, not checked
0150h Start of game

在这种特殊情况下,我们0147h对cartridge 类型的值感兴趣。如果cartridge 安装了 MBC1,cartridge 类型可以是以下值之一:

与 MBC1 相关的 Cartridge 类型值

Value Definition
00h No MBC
01h MBC1
02h MBC1 with external RAM
03h MBC1 with battery-backed external RAM

就本文而言,不会为外部 RAM 实施电池后备系统;这个特性经常被游戏用来保存它们的状态以备后用,我们将在后面的部分更详细地介绍。

MBC1的实现

存储库控制器显然是对内存的一种操作,因此非常适合 MMU。由于第一个 ROM 组(组 #0)是固定的,因此只需要为 MBC 维护一个偏移量,以指示第二个组的读取位置。为了允许稍后添加更多 MBC 处理,可以使用一组数据来保存给定控制器的状态:

MMU.js:MBC 状态和重置

MMU = {// MBC states_mbc: [],// Offset for second ROM bank_romoffs: 0x4000,// Offset for RAM bank_ramoffs: 0x0000,// Copy of the ROM's cartridge-type value_carttype: 0,reset: function(){...// In addition to previous reset code,// initialise MBC internal dataMMU._mbc[0] = {};MMU._mbc[1] = {rombank: 0,        // Selected ROM bankrambank: 0,     // Selected RAM bankramon: 0,       // RAM enable switchmode: 0     // ROM/RAM expansion mode};MMU._romoffs = 0x4000;MMU._ramoffs = 0x0000;},load: function(file){...MMU._carttype = MMU._rom.charCodeAt(0x0147);}
}

从上面的代码中可以看出,MBC1 的四个寄存器的内部状态由 MMU 内的一个对象表示,与 MBC 类型 1 相关联。当这些改变时,可以修改 ROM 和 RAM 偏移量以指向适当的记忆库;一旦设置了指针,对内存的访问几乎可以照常进行。

MMU.js:基于 MBC1 的访问

MMU = {rb: function(addr){switch(addr & 0xF000){...// ROM (switched bank)case 0x4000:case 0x5000:case 0x6000:case 0x7000:return MMU._rom.charCodeAt(MMU._romoffs +(addr & 0x3FFF));// External RAMcase 0xA000:case 0xB000:return MMU._eram[MMU._ramoffs +(addr & 0x1FFF)];}}
};

这些指针偏移量的计算是在写入 MBC 寄存器时进行的,如下所示。

MMU.js:MBC1控制器

    wb: function(addr, val){switch(addr & 0xF000){// MBC1: External RAM switchcase 0x0000:case 0x1000:switch(MMU._carttype){case 2:case 3:MMU._mbc[1].ramon =((val & 0x0F) == 0x0A) ? 1 : 0;break;}break;// MBC1: ROM bankcase 0x2000:case 0x3000:switch(MMU._carttype){case 1:case 2:case 3:// Set lower 5 bits of ROM bank (skipping #0)val &= 0x1F;if(!val) val = 1;MMU._mbc[1].rombank =(MMU._mbc[1].rombank & 0x60) + val;// Calculate ROM offset from bankMMU._romoffs = MMU._mbc[1].rombank * 0x4000;break;}break;// MBC1: RAM bankcase 0x4000:case 0x5000:switch(MMU._carttype){case 1:case 2:case 3:if(MMU._mbc[1].mode){// RAM mode: Set bankMMU._mbc[1].rambank = val & 3;MMU._ramoffs = MMU._mbc[1].rambank * 0x2000;}else{// ROM mode: Set high bits of bankMMU._mbc[1].rombank =(MMU._mbc[1].rombank & 0x1F) +((val & 3) << 5);MMU._romoffs = MMU._mbc[1].rombank * 0x4000;}break;}break;// MBC1: Mode switchcase 0x6000:case 0x7000:switch(MMU._carttype){case 2:case 3:MMU._mbc[1].mode = val & 1;break;}break;...// External RAMcase 0xA000:case 0xB000:MMU._eram[MMU._ramoffs + (addr & 0x1FFF)] = val;break;}}

在上面的控制代码中,声明为附加了外部 RAM 的 MBC1 实例是具有 RAM 存储区的实例。有了这段代码,图 1 所示的演示就可以正常加载并运行了;如果没有 MBC1 处理程序,代码将在尝试访问显示的精灵和背景数据时崩溃。

接下来

除了能够将更大的游戏放入内存之外,游戏更重要的方面之一是保持时间的能力:例如,基于时钟的游戏如果没有某种计时机制作为其时钟的基础就毫无用处. 如前所述,很多游戏都使用垂直消隐中断来做这个计时,但有些游戏需要更细粒度的时间结构;这是由 GameBoy 中的硬件计时器提供的,与 CPU 时钟相关联。

计时器还提供了一种检查 CPU 时钟的方法,这使它可用作随机数生成器的种子;例如,俄罗斯方块使用硬件定时器的这个功能来挑选它的块。在下一部分中,我将详细了解计时器的工作原理以及它的实现方式。

Game boy模拟器(9):内存池相关推荐

  1. linux内存分配 连续 足够,linux内存池能分配连续物理内存吗

    中. size参数: 内核是基于页技术分配内存,以最佳的利用系统的RAM. linux处理内存分配的方法是:创建一系列的内存对象池,每个池的内存大小事固定的,处理分配请求时,就直接在包含足够大的内存块 ...

  2. ceph bluestore源码分析:admin_socket实时获取内存池数据

    环境: 版本:ceph 12.2.1 部署完cephfs 使用ceph-fuse挂载,并写入数据 关键参数: debug_mempool = true 将该参数置为true即可查看详细的blustor ...

  3. Memcached内存池分析

    针对Memcacged1.4.15代码 1.完整slabs内存池图 这是我画的memcached的slabs内存池对象关系图: 2.内存池数据结构 typedef struct {unsigned i ...

  4. 提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存,必须在分配器方法中添加互斥锁. 全局内存管理器(通过new()和delete()实现)是通用的,因此它的开销也非常大. 因为单线程内存管理器要比多线程内存管理器快的 ...

  5. 提高C++性能的编程技术笔记:单线程内存池+测试代码

    频繁地分配和回收内存会严重地降低程序的性能.性能降低的原因在于默认的内存管理是通用的.应用程序可能会以某种特定的方式使用内存,并且为不需要的功能付出性能上的代价.通过开发专用的内存管理器可以解决这个问 ...

  6. 某内存池中的指针用法

    内存池实现有许多种,各有不同的优缺点. 这里不是主要说内存池,只是觉得这个内存池中的指针用得很飘逸! template <class T,int AllocSize = 50> class ...

  7. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  8. Nginx内存池实现的了解

    参考: http://blog.csdn.net/livelylittlefish/article/details/6586946 http://code.google.com/p/nginxsrp/ ...

  9. android内存池,两种常见的内存管理方法:堆和内存池

    描述 本文导读 在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据.若需将数据存储在内存中,以便进一步运算.处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空 ...

最新文章

  1. mysql与ofbiz,ofbiz+mysql安装求教
  2. java实现沙箱测试环境支付宝支付(demo)和整合微信支付和支付宝支付到springmvc+spring+mybatis环境全过程(支付宝和微信支付)
  3. html5发布原文,HTML5 第二份草案发布
  4. 给 UITextField 添加左侧指示图片(类似微信登录框)
  5. 如果不知道前路在哪儿,那就走好眼前路
  6. python画图如何调整图例位置_Python——legend()图例位置调整
  7. git init github
  8. 《Effective Java》第5条:避免创建不必要的对象
  9. 分析方法论_用户生命周期的建立
  10. windows小工具txt转xlsx
  11. 科普:卡他妈滤波_拔剑-浆糊的传说_新浪博客
  12. 数据库面试考题一览(全面覆盖)
  13. 2020年中山大学CS夏令营
  14. Linux Python 导航目录
  15. 5种最热门编程语言的优缺点
  16. (2020)Simple Copy-Paste is a Strong Data Augmentation Method for Instance Segmentation
  17. CheckM-Options-zn
  18. python把txt导入excel,python如何将txt文件导入Excel?
  19. MPU6500功能说明
  20. 从网易云音乐看爬虫。

热门文章

  1. Javaweb基于SSM的汽车维修管理系统
  2. size_t 与size_type的使用。。。。。
  3. c语言中register是局部变量吗,auto、static、register及全局变量和局部变量
  4. 苹果怎么应用分身_怎么在安卓手机上安装苹果的应用程序和游戏?你要了解的最佳方案...
  5. 停车场管理系统(数据库)
  6. 没有Nvidia(英伟达)显卡对深度学习计算的影响
  7. python工程师需要什么技能_江门北大青鸟:Python工程师都会哪些技能?
  8. 2016java程序设计大赛_2016湘潭大学首届JAVA程序设计竞赛
  9. 老猿学5G专栏文章目录
  10. 解决ValueError: Shape of passed values is (1,5), indices imply (1,3)