2013-06-29 3 views
0

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

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

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

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

+0

Это звучит так, как если бы таблица поиска была уместна. –

ответ

0

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

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

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

1

Однако 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-разрядных операндов, но, с другой стороны, ПК и большинство мобильных телефонов также основаны на малоэтажных процессорах.