Emulacja jest obszarem wieloaspektowym. Oto podstawowe pomysły i elementy funkcjonalne. Rozbiorę go na części, a następnie uzupełnię szczegóły poprzez edycję. Wiele rzeczy, które zamierzam opisać, będzie wymagać znajomości wewnętrznego działania procesorów - znajomość montażu jest konieczna. Jeśli jestem zbyt niejasny w niektórych kwestiach, zadawaj pytania, aby móc dalej poprawiać tę odpowiedź.
Podstawowy pomysł:
Emulacja działa poprzez obsługę zachowania procesora i poszczególnych komponentów. Budujesz każdy pojedynczy element systemu, a następnie łączysz elementy podobnie jak przewody w sprzęcie.
Emulacja procesora:
Istnieją trzy sposoby obsługi emulacji procesora:
- Interpretacja
- Rekompilacja dynamiczna
- Rekompilacja statyczna
Wszystkie te ścieżki mają ten sam ogólny cel: wykonać kawałek kodu, aby zmodyfikować stan procesora i wejść w interakcję ze „sprzętem”. Stan procesora to zlepek rejestrów procesora, programów obsługi przerwań itp. Dla danego celu procesora. Do 6502, trzeba kilka 8-bitowych liczb całkowitych reprezentujących rejestry: A
, X
, Y
, P
, i S
; miałbyś również PC
rejestr 16-bitowy .
Z interpretacją zaczynasz od IP
(wskaźnika instrukcji - nazywanego także PC
licznikiem programu) i odczytujesz instrukcję z pamięci. Kod analizuje tę instrukcję i wykorzystuje te informacje do zmiany stanu procesora zgodnie z określonym przez procesor. Podstawowym problemem interpretacji jest to, że jest bardzo powolna; za każdym razem, gdy wykonujesz daną instrukcję, musisz ją zdekodować i wykonać wymaganą operację.
Dzięki dynamicznej rekompilacji iterujesz kod, podobnie jak interpretacja, ale zamiast po prostu wykonywać opcodes, tworzysz listę operacji. Po dotarciu do instrukcji oddziału zestawiasz tę listę operacji z kodem maszynowym na platformie hosta, a następnie buforujesz ten skompilowany kod w pamięci podręcznej i uruchamiasz go. Następnie, gdy ponownie uderzysz w daną grupę instrukcji, musisz tylko wykonać kod z pamięci podręcznej. (BTW, większość ludzi tak naprawdę nie tworzy listy instrukcji, ale kompiluje je na bieżąco do kodu maszynowego - utrudnia to optymalizację, ale jest to poza zakresem tej odpowiedzi, chyba że wystarczająca liczba osób jest zainteresowana)
W przypadku rekompilacji statycznej robisz to samo co w rekompilacji dynamicznej, ale podążasz za gałęziami. W końcu budujesz fragment kodu, który reprezentuje cały kod w programie, który można następnie wykonać bez dalszych zakłóceń. Byłby to świetny mechanizm, gdyby nie następujące problemy:
- Kod, którego nie ma w programie na początek (np. Skompresowany, zaszyfrowany, wygenerowany / zmodyfikowany w czasie wykonywania itp.) Nie zostanie ponownie skompilowany, więc nie będzie działał
- Udowodniono, że znalezienie całego kodu w danym pliku binarnym jest równoznaczne z problemem zatrzymania
Dzięki temu statyczna rekompilacja jest całkowicie niemożliwa w 99% przypadków. Aby uzyskać więcej informacji, Michael Steil przeprowadził świetne badania nad rekompilacją statyczną - najlepsze, jakie widziałem.
Drugą stroną emulacji procesora jest sposób interakcji ze sprzętem. To naprawdę ma dwie strony:
- Czas procesora
- Przerwij obsługę
Czas procesora:
Niektóre platformy - zwłaszcza starsze konsole, takie jak NES, SNES itp. - wymagają od twojego emulatora ścisłego czasu, aby był w pełni kompatybilny. Dzięki NES masz PPU (procesor pikseli), który wymaga, aby procesor wstawiał piksele do swojej pamięci w określonych momentach. Jeśli korzystasz z interpretacji, możesz łatwo policzyć cykle i naśladować właściwy czas; dzięki rekompilacji dynamicznej / statycznej sprawy są bardziej skomplikowane.
Obsługa przerwań:
Przerwania są podstawowym mechanizmem komunikującym CPU ze sprzętem. Ogólnie rzecz biorąc, komponenty sprzętowe powiedzą procesorowi, na czym mu zależy. Jest to dość proste - kiedy twój kod generuje dane przerwanie, patrzysz na tabelę obsługi przerwań i wywołujesz odpowiednie wywołanie zwrotne.
Emulacja sprzętu:
Istnieją dwie strony emulacji danego urządzenia sprzętowego:
- Emulowanie funkcjonalności urządzenia
- Emulowanie rzeczywistych interfejsów urządzeń
Weźmy na przykład dysk twardy. Funkcjonalność jest emulowana poprzez tworzenie kopii zapasowej, procedur odczytu / zapisu / formatowania itp. Ta część jest na ogół bardzo prosta.
Rzeczywisty interfejs urządzenia jest nieco bardziej złożony. Jest to na ogół pewna kombinacja rejestrów zmapowanych w pamięci (np. Części pamięci, które urządzenie obserwuje pod kątem zmian w celu wykonania sygnalizacji) i przerywa. W przypadku dysku twardego może istnieć obszar mapowany w pamięci, w którym umieszcza się polecenia odczytu, zapisu itp., A następnie odczytuje te dane z powrotem.
Chciałbym zagłębić się w szczegóły, ale istnieje milion sposobów, w jakie możesz z tym pójść. Jeśli masz tutaj jakieś konkretne pytania, możesz je zadać, a ja dodam informacje.
Zasoby:
Myślę, że podałem tutaj całkiem niezłe wprowadzenie, ale jest mnóstwo dodatkowych obszarów. Z przyjemnością udzielę odpowiedzi na wszelkie pytania; Przez większość czasu byłem bardzo niejasny ze względu na ogromną złożoność.
Obowiązkowe linki w Wikipedii:
Ogólne zasoby emulacji:
- Zophar - tutaj zacząłem od emulacji, najpierw pobrałem emulatory, a potem splądrowałem ich ogromne archiwa dokumentacji. Jest to absolutnie najlepszy zasób, jaki możesz mieć.
- NGEmu - Niewiele bezpośrednich zasobów, ale ich fora są nie do pobicia.
- RomHacking.net - sekcja dokumentów zawiera zasoby dotyczące architektury maszyn dla popularnych konsol
Projekty emulatorów do odniesienia:
- IronBabel - jest to platforma emulacji dla platformy .NET, napisana w Nemerle i przekompilująca kod do C # w locie. Uwaga: To jest mój projekt, więc wybacz bezwstydną wtyczkę.
- BSnes - niesamowity emulator SNES z idealną dokładnością cyklu.
- MAME - emulator arcade. Świetne referencje.
- 6502asm.com - To jest emulator JavaScript 6502 z fajnym małym forum.
- dynarec'd 6502asm - To jest mały hack, który zrobiłem przez dzień lub dwa. Wziąłem istniejący emulator z 6502asm.com i zmieniłem go, aby dynamicznie przekompilować kod do JavaScript w celu znacznego zwiększenia prędkości.
Odniesienia do ponownej kompilacji procesora:
- Badania nad rekompilacją statyczną przeprowadzone przez Michaela Steila (przywołane powyżej) zakończyły się w tym artykule, a źródło i takie można znaleźć tutaj .
Uzupełnienie:
Minęło już ponad rok, odkąd ta odpowiedź została przesłana i przy całej tej uwagi, pomyślałem, że nadszedł czas, aby zaktualizować niektóre rzeczy.
Być może najbardziej ekscytującą rzeczą w emulacji jest teraz libcpu , zapoczątkowany przez wspomnianego Michaela Steila. Jest to biblioteka przeznaczona do obsługi dużej liczby rdzeni procesora, które używają LLVM do ponownej kompilacji (statycznej i dynamicznej!). Ma ogromny potencjał i myślę, że zrobi świetne rzeczy do emulacji.
Zwrócono mi również uwagę na emu-docs , w którym znajduje się świetne repozytorium dokumentacji systemu, które jest bardzo przydatne do celów emulacji. Nie spędziłem tam dużo czasu, ale wygląda na to, że mają wiele świetnych zasobów.
Cieszę się, że ten post był pomocny i mam nadzieję, że uda mi się zejść z tyłka i dokończyć książkę na ten temat do końca roku / na początku przyszłego roku.