Você não pode fazer isso em geral, mas, em alguns sentidos, pode muito bem, e houve alguns casos históricos em que você realmente precisou .
O Atari 2600 (ou Atari Video Computer System) foi um dos primeiros sistemas de videogame doméstico e foi lançado pela primeira vez em 1978. Ao contrário dos sistemas posteriores da época, a Atari não podia dar ao luxo de fornecer ao dispositivo um buffer de quadro, o que significa que a CPU tinha para executar o código em cada linha de verificação para determinar o que produzir - se esse código levasse 17,08 microssegundos para ser executado (o intervalo HBlank), os gráficos não seriam definidos corretamente antes que a linha de verificação começasse a desenhá-los. Pior, se o programador quisesse desenhar conteúdo mais complexo do que o Atari normalmente permitia, eles precisariam medir os horários exatos para obter instruções e alterar os registros gráficos à medida que o feixe estava sendo desenhado, com um intervalo de 57,29 microssegundos para toda a linha de varredura.
No entanto, o Atari 2600, como muitos outros sistemas baseados no 6502, tinha um recurso muito importante que possibilitou o gerenciamento cuidadoso do tempo necessário para este cenário: a CPU, a RAM e o sinal de TV funcionavam com o mesmo mestre. relógio. O sinal da TV disparou em um relógio de 3,98 MHz, dividindo os tempos acima em um número inteiro de "relógios coloridos" que gerenciavam o sinal da TV, e um ciclo dos relógios da CPU e da RAM eram exatamente três relógios coloridos, permitindo que o relógio da CPU fosse uma medida precisa do tempo em relação ao sinal atual da TV em andamento. (Para obter mais informações, consulte o Stella Programmer's Guide , escrito para o emulador Stella Atari 2600 ).
Além disso, esse ambiente operacional significava que todas as instruções de CPU tinham uma quantidade definida de ciclos necessários em todos os casos, e muitos 6502 desenvolvedores publicaram essas informações em tabelas de referência. Por exemplo, considere esta entrada para a CMP
instrução (Comparar memória com acumulador), obtida desta tabela :
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
Usando todas essas informações, o Atari 2600 (e outros desenvolvedores do 6502) conseguiu determinar exatamente quanto tempo o código estava demorando para executar e construir rotinas que fizessem o que precisavam e que ainda cumprissem os requisitos de tempo de sinal de TV da Atari. E como esse tempo era tão exato (especialmente para instruções de perda de tempo como o NOP), eles foram capazes de usá-lo para modificar os gráficos à medida que eram desenhados.
Obviamente, o 6502 do Atari é um caso muito específico, e tudo isso é possível apenas porque o sistema tinha o seguinte:
- Um relógio mestre que executava tudo, incluindo RAM. Os sistemas modernos têm relógios independentes para a CPU e a RAM, com o relógio da RAM geralmente sendo mais lento e os dois não necessariamente sincronizados.
- Sem cache de qualquer tipo - o 6502 sempre acessava a DRAM diretamente. Os sistemas modernos têm caches SRAM que dificultam a previsão do estado - embora talvez ainda seja possível prever o comportamento de um sistema com um cache, é definitivamente mais difícil.
- Nenhum outro programa foi executado simultaneamente - o programa no cartucho tinha controle total do sistema. Os sistemas modernos executam vários programas ao mesmo tempo usando algoritmos de determinação não determinísticos.
- Uma velocidade de clock lenta o suficiente para que os sinais possam viajar através do sistema a tempo. Em um sistema moderno com velocidades de clock de 4 GHz (por exemplo), são necessários um fóton de luz de 6,67 ciclos de clock para percorrer o comprimento de uma placa-mãe de meio metro - você nunca pode esperar que um processador moderno interaja com outra coisa na placa em apenas um ciclo, já que são necessários mais de um ciclo para que um sinal na placa alcance o dispositivo.
- Uma velocidade de clock bem definida que raramente muda (1,19 MHz no caso do Atari) - as velocidades da CPU dos sistemas modernos mudam o tempo todo, enquanto um Atari não poderia fazer isso sem afetar também o sinal da TV.
- Cronogramas de ciclo publicados - o x86 não define quanto tempo suas instruções demoram.
Todas essas coisas se uniram para criar um sistema onde era possível criar conjuntos de instruções que levavam um tempo exato - e para esse aplicativo, era exatamente isso que era exigido. A maioria dos sistemas não possui esse grau de precisão simplesmente porque não há necessidade - os cálculos são feitos quando são feitos ou, se for necessária uma quantidade exata de tempo, um relógio independente pode ser consultado. Mas se a necessidade for adequada (como em alguns sistemas incorporados), ela ainda poderá aparecer e você poderá determinar com precisão quanto tempo seu código leva para ser executado nesses ambientes.
E também devo acrescentar o grande aviso de isenção de responsabilidade de que tudo isso se aplica apenas à construção de um conjunto de instruções de montagem que levarão uma quantidade exata de tempo. Se o que você quer fazer é pegar uma peça de montagem arbitrária, mesmo nesses ambientes, e perguntar "Quanto tempo isso leva para ser executado?", Você categoricamente não pode fazer isso - esse é o Problema da Parada , que foi provado insolúvel.
EDIT 1: Em uma versão anterior desta resposta, afirmei que o Atari 2600 não tinha como informar ao processador onde estava o sinal de TV, o que o forçou a manter o programa inteiro contado e sincronizado desde o início. Conforme apontado nos comentários, isso é válido para alguns sistemas como o ZX Spectrum, mas o Atari 2600, pois contém um registro de hardware que interrompe a CPU até que ocorra o próximo intervalo de apagamento horizontal, bem como uma função para iniciar o intervalo de apagamento vertical à vontade. Portanto, o problema da contagem de ciclos é limitado a cada linha de verificação e só se torna exato se o desenvolvedor desejar alterar o conteúdo à medida que a linha de verificação estiver sendo desenhada.