C++ 模拟游戏控制台的内存映射,根据提供的地址访问不同的位置

C++ 模拟游戏控制台的内存映射,根据提供的地址访问不同的位置,c++,low-level,emulation,memory-mapping,C++,Low Level,Emulation,Memory Mapping,我正在为旧游戏机实现一个模拟器,主要是为了学习 这个控制台将ROM和许多其他东西映射到其地址空间内的区域。某些位置也会镜像,以便多个地址可以对应于同一物理位置。我想模仿这一点,但我不确定这样做的好方法是什么(也不知道这个过程叫什么,因此这个问题有点笼统) 有一件事是可行的,那就是一张简单的、无序的地图。让它包含绝对地址和指向我的数据结构的相应指针。通过这种方式,我可以轻松地将所需的一切映射到系统的地址空间。这种方法的问题在于,它显然是一种内存占用。即使是小ROM,由于前面提到的镜像,我最终也有近

我正在为旧游戏机实现一个模拟器,主要是为了学习

这个控制台将ROM和许多其他东西映射到其地址空间内的区域。某些位置也会镜像,以便多个地址可以对应于同一物理位置。我想模仿这一点,但我不确定这样做的好方法是什么(也不知道这个过程叫什么,因此这个问题有点笼统)

有一件事是可行的,那就是一张简单的、无序的地图。让它包含绝对地址和指向我的数据结构的相应指针。通过这种方式,我可以轻松地将所需的一切映射到系统的地址空间。这种方法的问题在于,它显然是一种内存占用。即使是小ROM,由于前面提到的镜像,我最终也有近1000万个条目。当然,这不可能是正确的做法吗

非常感谢您的帮助

编辑:

要提供有关我是如何做到这一点的一些详细信息:

当然,所讨论的系统是SNES。作为我的主要资源,我实现了我上面提到的如下内容:

  • 创建一个
    std::无序映射mMemoryMap
  • 检查rom是LoRom还是HiRom
  • 对于rom中的每个字节
    • 计算应该映射的地址,并在映射中放置地址和指向所述字节的指针
    • 如果该节需要在其他地方镜像,请重复上述步骤
  • 这将应用于我需要提供的任何其他内容,例如视频或系统内存

如果我现在想访问地址空间中的任何内容,我可以简单地使用系统内部使用的地址。

而不是使用简单的映射(即使范围可以扩大到较大),您可以使用更智能的映射


例如,如果控制台将
0x10XXXX
0x1FXXXX
全部映射到相同的
0x20XXXX
上,则可以设计一个保存该重复的结构(开始
0x100000
结束
0x1ffff
重复
0x010000
,尽管您可能希望使用位掩码而不是重复).

我假设对于连续地址,物理位置也是连续的,在某些内存块或“块”内。也就是说,如果地址0x0000映射到0xFF00,则0x0004映射到0xFF04

如果它们是这样工作的,那么您可以制作一个包含这些块的信息的列表。说:

struct Chunk
{
   int addressStart, memoryStart, size;
}

区块可以按addressStart排序,因此您可以找到任何地址所需的正确区块。这要求您迭代列表,但如果您只有几个块,这可能是可以接受的。

我目前与您处于同一条船上,正在做一个用于学习目的的NES模拟器。完整内存映射声明为指向字节的指针数组。每个指针都可以指向另一个数组,这样在镜像的情况下,我就可以拥有指向相同数据的指针

byte *memoryMap[cpuMemSize];
我对重复的地址进行迭代,并将它们映射到以下数组中的字节。ram阵列是在CPU内存映射中映射4次的内存

byte ram[ramSize];
下面的代码遍历RAM阵列,并将其映射到CPU内存映射4次

// map RAM 4 times to emulate mirroring
mem_address memAddr = 0;
for (int x = 0; x < 4; ++x)
{
    for (mem_address ramAddr = 0; ramAddr < ramSize; ++ramAddr.value)
    {
        memoryMap[memAddr.value++] = &ram[ramAddr.value];
    }
}

我还没有真正测试过这一点,我想做更多关于CPU缓存使用和性能的测试。当我无意中发现你的问题时,我显然是在寻找其他的方法来做这件事,我想我已经投入了我的(未经证实的)两分钱。希望这是有意义的。

我认为您应该为此添加代码,这是一个显而易见的解决方案,但我根本没有看到它。对结果非常满意。谢谢!大多数情况下,运行在模拟网元上的来宾代码将在正常内存上执行大部分加载/存储,其中连续地址在物理RAM中是连续的,并且使用我假设的相同内存映射。但是您选择的解决方案不允许任何额外的效率,并且为每次内存访问添加了额外级别的间接寻址,对每个字节分别执行。如果您为64位系统编译模拟器,则仿真RAM的每个字节占用4倍8字节的指针,或32倍的空间开销。这对于3到4GHz CPU上的网元来说是不错的,它的每核二级缓存速度比网元的RAM(包括典型的盒带)快,但肯定是非常低效的。考虑到CPU中的MMU硬件将一个页面表映射到物理地址,该页表每4K页映射一个8字节。我想你需要比4K页面更精细的粒度,但是如果你想使它更复杂+高效,你可以检查特殊地址,另外,通过指向同一阵列的4个指针之一访问模拟物理RAM。(或者在检查来宾地址是否为MMIO地址或其他特殊地址后,只需屏蔽它们的一些地址位。然后直接将它们用作物理数组的索引。如果这4个映射的大小不是2的幂次方,那么可能就不那么简单了,但使用编译时常量的模运算包装也是相当有效的。这是prec这就是为什么我一直在寻找/玩弄其他方法来进行这种映射。我意识到它有间接性,并且不会以缓存友好的方式打包数据,但我想我还是放弃这种方法,因为它“可以”对于像NES这样的低端系统,我提到缓存并不是因为它对缓存不友好,只是总体上是浪费。为了说明现代CPU有多强大,所以它们确实有足够的缓存来处理这个问题。但无论如何,你不需要检查“特殊”吗实际上是MMIO寄存器的地址?您可以检查完整地址,然后屏蔽
memAddr
的高位以获得
ramadr
,对吗?或者至少使用
*memoryMap[0x00ff] = 10;