Nie możesz tego ogólnie robić, ale pod pewnymi względami bardzo możesz, i było kilka historycznych przypadków, w których naprawdę musiałeś .
Atari 2600 (lub Atari wideo Computer System) był jednym z pierwszych systemów do gier wideo do domu i został po raz pierwszy wydany w 1978 roku W przeciwieństwie do późniejszych systemach ery Atari nie może sobie pozwolić, aby dać urządzeniem z bufora ramki, co oznacza, że procesor miał aby uruchomić kod na każdej linii skanowania, aby określić, co ma zostać wyprodukowane - jeśli ten kod zabrałby 17.08 mikrosekund do uruchomienia (interwał HBlank), grafika nie byłaby odpowiednio ustawiona, zanim linia skanowania zacznie je rysować. Co gorsza, jeśli programista chciał narysować bardziej złożoną zawartość niż normalnie zezwalały Atari, musiał mierzyć dokładne czasy instrukcji i zmieniać rejestry graficzne podczas rysowania wiązki, z rozpiętością 57,29 mikrosekund dla całej linii skanowania.
Jednak Atari 2600, podobnie jak wiele innych systemów opartych na 6502, miał bardzo ważną funkcję, która umożliwiła staranne zarządzanie czasem wymagane w tym scenariuszu: procesor, pamięć RAM i sygnał telewizyjny działały w oparciu o zegary oparte na tym samym urządzeniu głównym zegar. Sygnał telewizyjny pobiegł z zegarem 3,98 MHz, dzieląc powyższe czasy na całkowitą liczbę „kolorowych zegarów”, które zarządzały sygnałem telewizyjnym, a cykl procesora i zegarów pamięci RAM wynosił dokładnie trzy kolorowe zegary, co pozwala na ustawienie zegara procesora dokładna miara czasu w stosunku do bieżącego sygnału telewizyjnego postępu. (Aby uzyskać więcej informacji na ten temat, zapoznaj się z Przewodnikiem programisty Stella , napisanym dla emulatora Stella Atari 2600 ).
Ponadto to środowisko operacyjne oznaczało, że każda instrukcja CPU miała określoną liczbę cykli, którą zajmie w każdym przypadku, a wielu programistów 6502 opublikowało te informacje w tabelach referencyjnych. Rozważmy na przykład ten wpis instrukcji CMP
(Porównaj pamięć z akumulatorem) zaczerpnięty z tej tabeli :
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
Wykorzystując wszystkie te informacje, Atari 2600 (i inni programiści 6502) byli w stanie dokładnie określić, ile czasu zajmuje ich wykonanie kodu, i zbudować procedury, które zrobiły to, czego potrzebowały i nadal spełniały wymagania dotyczące czasu sygnału telewizyjnego Atari. A ponieważ czas był tak dokładny (szczególnie w przypadku marnujących czas instrukcji, takich jak NOP), byli w stanie nawet użyć go do modyfikacji grafiki podczas rysowania.
Oczywiście 6502 Atari jest bardzo specyficznym przypadkiem, a wszystko to jest możliwe tylko dlatego, że system miał wszystkie następujące elementy:
- Główny zegar, który uruchamiał wszystko, w tym pamięć RAM. Nowoczesne systemy mają niezależne zegary dla procesora i pamięci RAM, przy czym zegar RAM jest często wolniejszy, a dwa niekoniecznie są zsynchronizowane.
- Żadnego buforowania - 6502 zawsze uzyskuje bezpośredni dostęp do pamięci DRAM. Nowoczesne systemy mają pamięci podręczne SRAM, które utrudniają przewidywanie stanu - chociaż być może nadal można przewidzieć zachowanie systemu z pamięcią podręczną, jest to zdecydowanie trudniejsze.
- Żadne inne programy nie działają jednocześnie - program na kartridżu miał pełną kontrolę nad systemem. Nowoczesne systemy uruchamiają wiele programów jednocześnie, używając niedeterministycznych algorytmów szeregowania.
- Prędkość zegara na tyle wolna, że sygnały mogą przemieszczać się w systemie w czasie. W nowoczesnym systemie z zegarem 4 GHz (na przykład) potrzeba fotonu światła 6,67 cykli zegara, aby przejść przez półmetrową płytę główną - nigdy nie można oczekiwać, że nowoczesny procesor wejdzie w interakcję z czymś innym na płycie w jednym cyklu, ponieważ sygnał na płycie potrzebuje więcej niż jednego cyklu, aby dotrzeć nawet do urządzenia.
- Dobrze zdefiniowana prędkość zegara, która rzadko się zmienia (1,19 MHz w przypadku Atari) - prędkości procesorów nowoczesnych systemów zmieniają się cały czas, podczas gdy Atari nie mógł tego zrobić bez wpływu na sygnał telewizyjny.
- Opublikowane czasy cyklu - x86 nie określa, jak długo trwa jakakolwiek jego instrukcja.
Wszystkie te rzeczy połączyły się, aby stworzyć system, w którym można było stworzyć zestawy instrukcji, które zajęły dokładnie tyle czasu - i do tego zastosowania dokładnie tego wymagano. Większość systemów nie ma tego stopnia precyzji po prostu dlatego, że nie jest to konieczne - obliczenia albo są wykonywane, gdy są one wykonane, albo jeśli potrzebna jest dokładna ilość czasu, można zapytać o niezależny zegar. Ale jeśli potrzeba jest odpowiednia (na przykład w niektórych systemach wbudowanych), może się nadal pojawiać, a Ty będziesz w stanie dokładnie określić, ile czasu zajmuje uruchomienie kodu w tych środowiskach.
Powinienem również dodać duże, masowe zastrzeżenie, że wszystko to dotyczy tylko konstruowania zestawu instrukcji asemblacyjnych, które zajmą dokładnie tyle czasu. Jeśli chcesz zrobić dowolny zestaw, nawet w tych środowiskach, i zapytać: „Jak długo trwa to wykonanie?”, Kategorycznie nie możesz tego zrobić - to jest problem zatrzymania , który okazał się nierozwiązywalny.
EDYCJA 1: W poprzedniej wersji tej odpowiedzi stwierdziłem, że Atari 2600 nie ma możliwości poinformowania procesora o tym, gdzie jest w sygnale telewizyjnym, co zmusiło go do utrzymania liczenia i synchronizacji całego programu od samego początku. Jak wskazano mi w komentarzach, dotyczy to niektórych systemów, takich jak ZX Spectrum, ale nie jest tak w przypadku Atari 2600, ponieważ zawiera rejestr sprzętowy, który zatrzymuje procesor do momentu wystąpienia następnego interwału wygaszania poziomego, a także funkcja pozwalająca na rozpoczęcie pionowego interwału wygaszania do woli. W związku z tym problem zliczania cykli jest ograniczony do każdej linii skanowania i staje się dokładny tylko wtedy, gdy programista chce zmienić zawartość podczas rysowania linii skanowania.