Извлечение опкодов переменной длины и времени CPU

В настоящее время я пытаюсь написать эмулятор NES на C ++ как летний проект по программированию, чтобы подготовиться к осеннему семестру в следующем учебном году (я давно не программировал). Я уже написал эмулятор Chip8, поэтому я подумал, что следующим шагом будет попытка написать эмулятор NES.

В любом случае, я застреваю. я использую этот сайт для моей таблицы кодов операций, и я сталкиваюсь с дорожным блоком. На Chip8 все коды операций были длиной два байта, поэтому их было легко получить. Тем не менее, кажется, что NES имеет 2-х или 3-х байтовые коды операций, в зависимости от того, в каком режиме адресации находится процессор. Я не могу придумать простой способ выяснить, сколько байтов мне нужно прочитать для каждого кода операции (моя единственная идея была создать действительно длинные операторы if, которые проверяют первый байт кода операции, чтобы увидеть, сколько еще байтов нужно прочитать).

У меня также возникают проблемы с подсчетом циклов. Как создать часы на языке программирования, чтобы все было синхронизировано?

С другой стороны, поскольку NES является прямым порядком байтов, нужно ли мне читать programCounter + 1, а затем читать programCounter, чтобы получить правильный код операции?

0

Решение

Тем не менее, кажется, что NES имеет 2-х или 3-х байтовые коды операций, в зависимости от того, в каком режиме адресации находится процессор. Я не могу придумать простого способа узнать, сколько байтов мне нужно прочитать для каждого кода операции.

Код операции по-прежнему только один байт. Дополнительные байты определяют операнды для тех инструкций, которые имеют явные операнды.
Чтобы сделать декодирование, вы можете создать switch-блок с 256 случаями (на самом деле это не будет 256 случаев, потому что некоторые коды операций недопустимы). Это может выглядеть примерно так:

opcode = ReadByte(PC++);
switch (opcode) {
...
case 0x4C:              // JMP abs
address = ReadByte(PC++);
address |= (uint16_t)ReadByte(PC) << 8;
PC = address;
cycles += 3;
break;
...
}

Компилятор, как правило, создает таблицу переходов для случаев, так что в итоге вы получите довольно эффективный (хотя и слегка раздутый) код.

Другой альтернативой является создание массива с одной записью на код операции. Это может быть просто массив указателей на функции с одной функцией на код операции — или таблица может содержать указатель на одну функцию для выборки операндов, одну для выполнения фактической операции, плюс информацию о количестве циклов, необходимых для инструкции. Таким образом, вы можете поделиться большим количеством кода. Пример:

const Instruction INSTRUCTIONS[] =
{
...
// 0x4C: JMP abs
{&jmp, &abs_operand, 3},
...
};

У меня также возникают проблемы с подсчетом циклов. Как создать часы на языке программирования, чтобы все было синхронизировано?

Подсчет циклов ЦП — это просто увеличение счетчика, как я показал в моих примерах кода выше.

Чтобы синхронизировать видео с процессором, самый простой способ — запустить процессор на количество циклов, соответствующее активному периоду отображения одной строки развертки, затем нарисовать одну строку развертки, а затем запустить процессор на количество циклов, соответствующее горизонтали. период гашения и начать заново.

Когда вы начнете использовать аудио, то как вы синхронизируете вещи, может немного зависеть от используемого вами аудио API. Например, некоторые API могут отправлять вам обратный вызов, на который вы отвечаете, заполняя буфер сэмплами и возвращая количество сгенерированных сэмплов. В этом случае вы можете рассчитать количество циклов ЦП, которые были эмулированы с момента предыдущего обратного вызова, и определить, сколько сэмплов сгенерировать на основе этого.


С другой стороны, поскольку NES имеет младший порядок, нужно ли мне читать programCounter + 1, а затем читать programCounter, чтобы получить правильный код операции?

Поскольку код операции — это один байт, а инструкции на 6502 не упакованы в одно слово, как на некоторых других архитектурах ЦП, порядок байтов не имеет значения. Это делает становятся актуальными для 16-битных операндов, но, с другой стороны, ПК и большинство мобильных телефонов также основаны на процессорах с прямым порядком байтов.

1

Другие решения

Я написал эмулятор для 6502 около 25 лет назад.

Это довольно простой процессор, поэтому либо таблица указателей функций, либо переключатель с 256 записями для байтов [переключатель может быть немного короче, поскольку во всех 256 записях нет действительных кодов операций, только около 200 из кодов операций на самом деле используются].

Теперь, если вы хотите написать симулятор, который точно симулирует время выполнения инструкций, тогда вам будет веселее. Вам, в основном, придется смоделировать гораздо больше того, как работает каждый компонент, и «проколоть» единицы измерения с помощью часов. Это довольно трудоемкая работа, поэтому я, возможно, по возможности проигнорирую синхронизацию и просто позволю скорости системы зависеть от скорости эмуляторов.

0

По вопросам рекламы [email protected]