В свободное время я начинаю писать очень простой эмулятор C ++ для процессора 6502.
Раньше я записывал много ассемблерного кода для этого процессора, поэтому все коды операций, режимы адресации и прочие мелочи не имеют большого значения.
6502 имеет 56 различных команд и 13 режимов адресации, что дает в общей сложности 151 код операции. Для меня скорость не проблема, поэтому вместо написания огромного оператора switch-case и повторения одного и того же кода снова и снова (разные коды операций могут ссылаться на одну и ту же инструкцию, используя другой режим адресации), я хотел бы отделить реальный код инструкции от код режима адресации: я нашел это решение очень аккуратным, так как для этого нужно было бы написать только 13 функций режима адресации и 56 функций инструкций без повторения.
здесь функционирует режим адресации:
// Addressing modes
uint16_t Addr_ACC(); // ACCUMULATOR
uint16_t Addr_IMM(); // IMMEDIATE
uint16_t Addr_ABS(); // ABSOLUTE
uint16_t Addr_ZER(); // ZERO PAGE
uint16_t Addr_ZEX(); // INDEXED-X ZERO PAGE
uint16_t Addr_ZEY(); // INDEXED-Y ZERO PAGE
uint16_t Addr_ABX(); // INDEXED-X ABSOLUTE
uint16_t Addr_ABY(); // INDEXED-Y ABSOLUTE
uint16_t Addr_IMP(); // IMPLIED
uint16_t Addr_REL(); // RELATIVE
uint16_t Addr_INX(); // INDEXED-X INDIRECT
uint16_t Addr_INY(); // INDEXED-Y INDIRECT
uint16_t Addr_ABI(); // ABSOLUTE INDIRECT
все они возвращают фактический адрес памяти (16 бит), используемый инструкцией для чтения / записи операнда / результата
прототип функции инструкции:
void Op_ADC(uint16_t addr);
void Op_AND(uint16_t addr);
void Op_ASL(uint16_t addr);
...
он берет 16-битный адрес, выполняет свои собственные операции, обновляет флаги состояния и / или регистры и фиксирует результаты (если таковые имеются) на одном и том же адресе памяти.
Учитывая эту структуру кода, мне было трудно использовать режим адресации АККУМУЛЯТОРА, который является единственным, который возвращает действительное значение внутреннего регистра А вместо адреса памяти. Я мог бы вернуть значение A, используя тип возвращаемого значения uin16_t, и добавить логический флаг для такого режима адресации, но я считаю его крайне уродливым решением.
Функции команд должны быть полностью независимыми от режима адресации.
В Sharp6502 (мой механизм эмуляции 6502, написанный на C #) я рассматриваю внутренние регистры и внешнюю память как объекты первого класса — класс MemoryManager создает объект для внешней памяти, а другой — для внутренних регистров, сопоставленных с другим числовым диапазоном. Следовательно, доступ к памяти и доступ к регистрам идентичны на функциональном уровне, поскольку на них оба ссылаются через MemoryManager в соответствии с тем, что в основном является индексом.
Дифференцирование в режиме адресации — это просто вопрос фильтрации битовой комбинации эмулируемой команды и выполнения очень простого вычисления для определения индекса, который должен быть передан в MemoryManager — это может подразумеваться или требовать один или два дополнительных байта, но Базовый механизм идентичен для каждой инструкции.
Других решений пока нет …