Можно ли процедурным образом определить количество циклов, выполняемых конкретной инструкцией на 6502?
Большинство эмуляторов хранят количество циклов, выполняемых конкретной инструкцией, в справочной таблице, а затем добавляют любые условные циклы, если необходимо (например, при пересечении границ страницы).
Мне интересно, есть ли способ процедурно определить количество циклов, которые будет выполнять инструкция, основываясь исключительно на режиме адресации и чтении / записи памяти.
Чтобы привести пример, я заметил, что все инструкции, которые используют непосредственную или относительную адресацию, занимают 2 цикла.
Все инструкции нулевой страницы занимают 3 цикла, плюс дополнительные 2 цикла, если изменение памяти на месте.
Все проиндексированные инструкции нулевой страницы занимают 4 цикла, плюс дополнительные 2 цикла, если изменение памяти на месте.
...И так далее.
Итак, есть ли какой-нибудь полностью документированный, процедурный способ определения количества циклов для инструкции, подобной приведенной выше? Существуют ли исключения, которые нарушают детерминизм в такой формуле?
1 ответ
Да - и так написаны почти все точные эмуляторы *; см. документы, такие как 64doc.txt. Это не намного сложнее, чем простой подсчет доступа к памяти - 6502 будет выполнять доступ к памяти каждый цикл, обычно он может получить значимый результат в течение оставшейся части цикла после доступа (т.е. я немного машу рукой, чтобы избежать обсуждение того, что конвейеризировано, а что нет; см. документацию).
Так, например, для ADC #54
процессор должен (i) прочитать код операции; (ii) читать операнд. Это два цикла.
За ADC ($32), Y
это:
- читать код операции
- читать операнд
- читать с $32, чтобы получить младший байт адреса
- прочитайте от $33, чтобы получить старший байт адреса, добавьте Y к младшему байту адреса
- чтение из (старший байт адреса):(младший байт адреса + Y), так как было только время для выполнения вычисления младшего байта
- о, подождите, если был перенос, то последний результат был неправильным, лучше перечитайте еще раз. Если нет, то отлично, все хорошо, не беспокойтесь об этом цикле.
Так что это либо 5, либо 6 циклов.
Вы всегда можете эмулировать доступ к памяти больше как пошаговую синхронизированную вещь и выполнять фактическую операцию как ортогональный шаг. Также легко использовать одну и ту же логику для чтения, записи или чтения-изменения-записи: чтение и запись имеют одинаковое время, но в конце имеют разный доступ к памяти, все команды tead-modify-write записывают значение чтения обратно для цикла Разрабатывая реальный результат, напишите реальный результат.
*) поскольку одновременное выполнение всех обращений к памяти, за исключением избыточных, то небольшое искажение времени вперед абсолютно не похоже на реальное аппаратное обеспечение. И это сбивает вас с толку, как только доступ к памяти будет связан с чем-либо с независимой концепцией времени - таймером или чем-то, что может генерировать прерывания, или просто самой ОЗУ, если машина сканирует ОЗУ для вывода видео; не берите в голову, что это требует, чтобы вы добавили специальные случаи вокруг инструкций как CLI
а также SEI
**. Эмуляторы больше не нужно структурировать, как в 1990-х годах.
**) Состояние IRQ определяется в предпоследнем цикле каждой операции. CLI
а также SEI
отрегулируйте бит во время последнего цикла. Так что даже если прерывание ожидает, то CLI
не приведет к прерыванию, пока после инструкции после CLI
, Который сам может быть SEI
, Так что CLI
/SEI
пара в ожидании прерывания должна привести к отключению обработчика прерываний после SEI
выполнено с установленным флагом прерывания. Это происходит естественным образом, если вы эмулируете циклическое поведение 6502, как правило, становится огромным хаком, если вы работаете по принципу "операция за операцией" и искажением времени. Или, что более вероятно, такие эмуляторы просто неправильно понимают поведение.