Non puoi farlo in generale, ma in un certo senso puoi farlo davvero, e ci sono stati alcuni casi storici in cui davvero dovevi farlo.
L' Atari 2600 (o Atari Video Computer System) era uno dei primi sistemi di videogiochi domestici ed è stato rilasciato per la prima volta nel 1978. A differenza dei sistemi successivi dell'epoca, Atari non poteva permettersi di fornire al dispositivo un frame buffer, nel senso che la CPU aveva per eseguire il codice su ogni scanline per determinare cosa produrre - se questo codice impiegasse 17.08 microsecondi (l'intervallo HBlank), la grafica non verrebbe impostata correttamente prima che la scanline iniziasse a disegnarli. Peggio ancora, se il programmatore voleva disegnare contenuti più complessi di quanto normalmente consentito dall'Atari, doveva misurare i tempi esatti per le istruzioni e cambiare i registri grafici mentre veniva disegnato il raggio, con un arco di 57.29 microsecondi per l'intera linea di scansione.
Tuttavia, l'Atari 2600, come molti altri sistemi basati sul 6502, aveva una caratteristica molto importante che consentiva la gestione attenta del tempo richiesta per questo scenario: la CPU, la RAM e il segnale TV funzionavano tutti con un clock basato sullo stesso master orologio. Il segnale TV scorreva su un orologio da 3,98 MHz, suddividendo i tempi sopra in un numero intero di "orologi a colori" che gestivano il segnale TV, e un ciclo di orologi CPU e RAM era esattamente tre orologi a colori, consentendo al clock della CPU di essere una misura precisa del tempo relativa all'attuale segnale TV di avanzamento. (Per ulteriori informazioni al riguardo, consulta la Guida del programmatore Stella , scritta per l' emulatore Stella Atari 2600 ).
Questo ambiente operativo, inoltre, significava che ogni istruzione CPU aveva una quantità definita di cicli che avrebbe richiesto in ogni caso, e molti 6502 sviluppatori hanno pubblicato queste informazioni nelle tabelle di riferimento. Ad esempio, considera questa voce per l' CMP
istruzione (Confronta memoria con accumulatore), presa da questa tabella :
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
Utilizzando tutte queste informazioni, Atari 2600 (e altri 6502 sviluppatori) sono stati in grado di determinare con esattezza il tempo impiegato per l'esecuzione del loro codice e di costruire routine in grado di soddisfare le esigenze di temporizzazione del segnale TV di Atari. E poiché questo tempismo era così esatto (specialmente per istruzioni che fanno perdere tempo come NOP), sono stati persino in grado di usarlo per modificare la grafica mentre venivano disegnati.
Naturalmente, l'Atari 6502 è un caso molto specifico, e tutto ciò è possibile solo perché il sistema aveva tutto quanto segue:
- Un master clock che eseguiva tutto, compresa la RAM. I sistemi moderni hanno clock indipendenti per CPU e RAM, con il clock della RAM spesso più lento e i due non necessariamente sincronizzati.
- Nessuna memorizzazione nella cache di alcun tipo: il 6502 accedeva sempre direttamente alla DRAM. I sistemi moderni hanno cache SRAM che rendono più difficile prevedere lo stato - mentre è forse ancora possibile prevedere il comportamento di un sistema con una cache, è sicuramente più difficile.
- Nessun altro programma in esecuzione contemporaneamente: il programma sulla cartuccia aveva il controllo completo del sistema. I sistemi moderni eseguono più programmi contemporaneamente utilizzando algoritmi di pianificazione non deterministici.
- Una velocità di clock abbastanza bassa da consentire ai segnali di viaggiare nel tempo nel sistema. Su un sistema moderno con velocità di clock di 4 GHz (ad esempio), sono necessari un fotone di luce 6,67 cicli di clock per percorrere la lunghezza di una scheda madre di mezzo metro: non ci si può aspettare che un processore moderno interagisca con qualcos'altro sulla scheda in un solo ciclo, poiché è necessario più di un ciclo affinché un segnale sulla scheda raggiunga anche il dispositivo.
- Una velocità di clock ben definita che cambia raramente (1,19 MHz nel caso dell'Atari): le velocità della CPU dei sistemi moderni cambiano continuamente, mentre un Atari non può farlo senza influenzare anche il segnale TV.
- Tempi di ciclo pubblicati: x86 non definisce quanto tempo impiegano le sue istruzioni.
Tutte queste cose si sono unite per creare un sistema in cui era possibile creare serie di istruzioni che richiedevano un esatto ammontare di tempo - e per questa applicazione, questo è esattamente ciò che è stato richiesto. La maggior parte dei sistemi non ha questo grado di precisione semplicemente perché non ce n'è bisogno: i calcoli vengono eseguiti quando vengono eseguiti o se è necessario un tempo esatto, è possibile eseguire una query su un orologio indipendente. Ma se la necessità è giusta (come su alcuni sistemi integrati), può ancora apparire e sarai in grado di determinare con precisione il tempo necessario per l'esecuzione del codice in questi ambienti.
E dovrei anche aggiungere l'enorme dichiarazione di non responsabilità che tutto ciò si applica solo alla costruzione di una serie di istruzioni di assemblaggio che richiederanno un esatto tempo. Se quello che vuoi fare è prendere un pezzo arbitrario di assemblaggio, anche in questi ambienti, e chiedere "Quanto tempo richiede l'esecuzione?", Non puoi categoricamente farlo - questo è il problema di Halting , che è stato dimostrato irrisolvibile.
EDIT 1: In una versione precedente di questa risposta, ho affermato che l'Atari 2600 non aveva modo di informare il processore di dove si trovasse nel segnale TV, il che lo costringeva a mantenere l'intero programma contato e sincronizzato sin dall'inizio. Come ho sottolineato nei commenti, questo è vero per alcuni sistemi come lo ZX Spectrum, ma non è vero per l'Atari 2600, poiché contiene un registro hardware che arresta la CPU fino a quando si verifica il successivo intervallo di blanking orizzontale, nonché una funzione per iniziare a piacere l'intervallo di soppressione verticale. Pertanto, il problema dei cicli di conteggio è limitato a ciascuna linea di scansione e diventa esatto solo se lo sviluppatore desidera cambiare contenuto mentre viene disegnata la linea di scansione.