Я реализую эмулятор для старой игровой консоли, в основном для учебных целей.
Эта консоль отображает ромы и многое другое в регионы своего адресного пространства. Определенные местоположения также отражаются, так что несколько адресов могут соответствовать одному физическому местоположению. Я хотел бы подражать этому, но я не уверен, что было бы хорошим подходом для этого (и также не знаю, как называется этот процесс, отсюда и этот несколько общий вопрос).
Одна вещь, которая работает, это простая неупорядоченная карта. Содержат ли они абсолютные адреса и соответствующие указатели на мои структуры данных. Таким образом, я могу легко отобразить все, что мне нужно, в адресное пространство системы. Проблема с этим подходом заключается в том, что, очевидно, это боров памяти. Даже с небольшими ромами я получаю около десяти миллионов записей благодаря вышеупомянутому зеркалированию. Конечно, это не может быть самым сложным делом?
Буду признателен за любую оказанную помощь.
Редактировать:
Чтобы предоставить некоторые детали относительно того, как именно я делаю это:
Рассматриваемая система — это, конечно, SNES. С помощью эта вики в качестве основного ресурса я реализовал то, что упомянул выше:
std::unordered_map<uint32,uint8_t*> mMemoryMap;
Если я теперь хочу получить доступ к чему-либо в адресном пространстве, я могу просто использовать адрес, который система будет использовать внутри.
Я предполагаю, что для смежных адресов физические местоположения также являются смежными, в определенных блоках памяти или «кусках». То есть, если адрес 0x0000 отображается на 0xFF00, то 0x0004 отображается на 0xFF04.
Если они так работают, то вы можете составить список, содержащий информацию об этих чанках. Сказать:
struct Chunk
{
int addressStart, memoryStart, size;
}
Чанки могут быть упорядочены по адресу addressStart, поэтому вы можете найти правильный чанк, который вам понадобится для любого адреса. Это требует от вас повторения списка, но если у вас есть только несколько кусков, это может быть приемлемым.
Вместо того, чтобы использовать простые карты (которые даже с диапазонами могут увеличиться до больших размеров), вы можете вместо этого использовать более интеллектуальную карту.
Например, если консоль сопоставляет 0x10XXXX
через 0x1FXXXX
все к тому же 0x20XXXX
Вы можете спроектировать структуру, которая содержит это повторение (начало 0x100000
конец 0x1FFFFF
повторение 0x010000
хотя вы можете использовать битовую маску, а не повторять).
В настоящее время я нахожусь в той же лодке, что и вы, и выполняю эмулятор NES в учебных целях. Полная карта памяти объявляется как массив указателей на байты. Каждый указатель может указывать на другой массив, что позволяет мне иметь указатели на те же данные в случае зеркального отображения.
byte *memoryMap[cpuMemSize];
Я перебираю адреса, которые повторяются, и отображаю их, чтобы они указывали на байты в следующем массиве. Массив оперативной памяти — это память, которая отображается 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];
}
}
Затем вы можете записать значение в 256-й байт, используя что-то вроде этого, что, конечно, будет распространено на другие части карты памяти, поскольку они указывают на один и тот же байт в памяти:
*memoryMap[0x00ff] = 10;
Я на самом деле не проверял это и хочу провести дополнительное тестирование в отношении использования кэша процессора и производительности. Я явно искал другие способы сделать это, когда наткнулся на ваш вопрос и решил, что я положу свои (непроверенные) два цента. Надеюсь, это имеет смысл.