Вы не можете сделать это в целом, но в некоторых отношениях вы очень можете, и было несколько исторических случаев, в которых вы действительно должны были.
Atari 2600 (или Atari Видеосистема компьютера) был один из самых ранних домашнего видео игровых систем и впервые была выпущена в 1978 г. В отличие от более поздних систем эпохи, Atari не может позволить себе , чтобы дать устройству буфер кадра, а это означает , что процессор имел запускать код на каждой линии сканирования, чтобы определить, что производить - если выполнение этого кода занимает более 17,08 микросекунд (интервал HBlank), графика не будет правильно настроена до того, как линия сканирования начнет их рисовать. Хуже того, если программист хотел нарисовать более сложный контент, чем обычно разрешал Atari, он должен был измерить точное время для инструкций и изменить графические регистры во время прорисовки луча с интервалом 57,29 микросекунд для всей строки сканирования.
Однако Atari 2600, как и многие другие системы, основанные на 6502, имел очень важную функцию, обеспечивающую тщательное управление временем, требуемое для этого сценария: центральный процессор, оперативная память и телевизионный сигнал запускались с тактовой частотой на основе одного и того же мастера. Часы. Телевизионный сигнал проходил с тактовой частоты 3,98 МГц, разделяя приведенное выше время на целое число «цветовых часов», которые управляли телевизионным сигналом, а цикл тактовых импульсов ЦП и ОЗУ составлял ровно три цветовых тактовых сигнала, позволяя такту ЦП быть точная мера времени относительно текущего телевизионного сигнала прогресса. (За дополнительной информацией обращайтесь к Руководству программиста Stella , написанному для эмулятора Stella Atari 2600 ).
Эта операционная среда, кроме того, означала, что каждая инструкция ЦП имела определенное количество циклов, которое потребуется в каждом случае, и многие 6502 разработчика опубликовали эту информацию в справочных таблицах. Например, рассмотрим эту запись для CMP
инструкции (Сравнить память с аккумулятором), взятой из этой таблицы :
CMP Compare Memory with Accumulator
A - M N Z C I D V
+ + + - - -
addressing assembler opc bytes cycles
--------------------------------------------
immediate CMP #oper C9 2 2
zeropage CMP oper C5 2 3
zeropage,X CMP oper,X D5 2 4
absolute CMP oper CD 3 4
absolute,X CMP oper,X DD 3 4*
absolute,Y CMP oper,Y D9 3 4*
(indirect,X) CMP (oper,X) C1 2 6
(indirect),Y CMP (oper),Y D1 2 5*
* add 1 to cycles if page boundary is crossed
Используя всю эту информацию, Atari 2600 (и другие разработчики 6502) смогли точно определить, сколько времени потребовалось для выполнения их кода, и создать подпрограммы, которые выполняли то, что им было нужно, и при этом соответствовали требованиям синхронизации телевизионных сигналов Atari. И поскольку это время было настолько точным (особенно для инструкций, которые тратят время, например, NOP), они даже смогли использовать его для изменения графики во время рисования.
Конечно, 6502 Atari - это очень специфический случай, и все это возможно только потому, что в системе было все следующее:
- Основные часы, которые запускали все, включая оперативную память. Современные системы имеют независимые тактовые частоты для процессора и оперативной памяти, причем тактовая частота ОЗУ часто медленнее, а две не обязательно синхронизированы.
- Никакого кеширования - 6502 всегда обращался к DRAM напрямую. Современные системы имеют кэши SRAM, которые затрудняют прогнозирование состояния - хотя, возможно, все еще можно предсказать поведение системы с кешем, это, безусловно, более сложно.
- Другие программы не запускаются одновременно - программа на картридже полностью контролировала систему. Современные системы запускают несколько программ одновременно, используя недетерминированные алгоритмы планирования.
- Тактовая частота достаточно низкая, чтобы сигналы могли распространяться по системе во времени. В современной системе с тактовой частотой 4 ГГц (например) требуется 6,67 такта фотона света, чтобы преодолеть длину полуметровой материнской платы - вы никогда не ожидаете, что современный процессор будет взаимодействовать с чем-то еще на плате всего за один цикл, поскольку для того, чтобы сигнал на плате даже достиг устройства, требуется более одного цикла.
- Точно определенная тактовая частота, которая редко изменяется (1,19 МГц в случае Atari) - частоты процессора современных систем постоянно меняются, в то время как Atari не может сделать это без воздействия на телевизионный сигнал.
- Опубликованные тайминги цикла - x86 не определяет, сколько времени занимает любая из его инструкций.
Все эти вещи собрались вместе, чтобы создать систему, в которой можно было создавать наборы инструкций, которые занимали точное время - и для этого приложения это именно то, что требовалось. Большинство систем не имеют такой степени точности просто потому, что в этом нет необходимости - расчеты либо выполняются, когда они выполняются, либо, если требуется точное количество времени, могут запрашиваться независимые часы. Но если необходимость правильная (например, в некоторых встроенных системах), она все равно может появиться, и вы сможете точно определить, сколько времени потребуется вашему коду для выполнения в этих средах.
И я должен также добавить большой массовый отказ от ответственности, который все это относится только к созданию набора инструкций по сборке, который займет точное количество времени. Если то, что вы хотите сделать, это взять какой-то произвольный фрагмент сборки, даже в этих средах, и спросить «Сколько времени это займет для выполнения?», Вы категорически не можете этого сделать - это проблема остановки , которая доказала свою неразрешимость.
РЕДАКТИРОВАТЬ 1: В предыдущей версии этого ответа я утверждал, что Atari 2600 не мог информировать процессор о том, где он находится в телевизионном сигнале, что вынудило его вести подсчет и синхронизацию всей программы с самого начала. Как отмечалось в комментариях, это относится к некоторым системам, таким как ZX Spectrum, но не относится к Atari 2600, поскольку он содержит аппаратный регистр, который останавливает процессор до следующего интервала горизонтального гашения, а также функция для начала вертикального интервала гашения по желанию. Следовательно, проблема подсчета циклов ограничена каждой строкой развертки и становится точной только в том случае, если разработчик желает изменить содержимое по мере отрисовки линии развертки.