Jak działają emulatory? Kiedy widzę emulatory NES / SNES lub C64, to mnie zadziwia.
Czy musisz emulować procesor tych maszyn, interpretując poszczególne instrukcje montażu? Co jeszcze w to wchodzi? Jak są zazwyczaj zaprojektowane?
Czy możesz udzielić porady osobom zainteresowanym pisaniem emulatorów (szczególnie systemu gier)?
Odpowiedzi:
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:
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
, iS
; miałbyś równieżPC
rejestr 16-bitowy .Z interpretacją zaczynasz od
IP
(wskaźnika instrukcji - nazywanego takżePC
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:
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:
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:
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:
Projekty emulatorów do odniesienia:
Odniesienia do ponownej kompilacji procesora:
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.
źródło
Facet o imieniu Victor Moya del Barrio napisał swoją pracę magisterską na ten temat. Wiele dobrych informacji na 152 stronach. Możesz pobrać plik PDF tutaj .
Jeśli nie chcesz się rejestrować w scribd , możesz znaleźć w Google tytuł PDF „Studium technik programowania emulacji” . Istnieje kilka różnych źródeł pliku PDF.
źródło
Emulacja może wydawać się zniechęcająca, ale w rzeczywistości jest znacznie łatwiejsza niż symulacja.
Każdy procesor zazwyczaj ma dobrze napisaną specyfikację opisującą stany, interakcje itp.
Jeśli w ogóle nie dbałeś o wydajność, możesz łatwo emulować większość starszych procesorów za pomocą bardzo eleganckich programów obiektowych. Na przykład procesor X86 potrzebowałby czegoś do utrzymania stanu rejestrów (łatwy), czegoś do utrzymania stanu pamięci (łatwy) i czegoś, co wziąłoby każde przychodzące polecenie i zastosowałoby to do bieżącego stanu maszyny. Jeśli naprawdę chcesz dokładności, możesz także emulować tłumaczenia pamięci, buforowanie itp., Ale jest to wykonalne.
W rzeczywistości wielu producentów mikroczipów i procesorów testuje programy na emulatorze układu, a następnie na samym układzie, co pomaga im dowiedzieć się, czy występują problemy ze specyfikacją układu lub z faktyczną implementacją układu w sprzęcie. Na przykład można napisać specyfikację układu, która spowodowałaby zakleszczenia, a kiedy termin pojawia się w sprzęcie, ważne jest, aby sprawdzić, czy można go odtworzyć w specyfikacji, ponieważ wskazuje to na większy problem niż coś w implementacji układu.
Oczywiście, emulatory gier wideo zazwyczaj dbają o wydajność, więc nie używają naiwnych implementacji, a także zawierają kod, który współpracuje z systemem operacyjnym hosta, na przykład do rysowania i dźwięku.
Biorąc pod uwagę bardzo niską wydajność starych gier wideo (NES / SNES itp.), Emulacja jest dość łatwa w nowoczesnych systemach. W rzeczywistości jest jeszcze bardziej niesamowite, że możesz po prostu pobrać zestaw każdej gry SNES w historii lub dowolnej gry Atari 2600, biorąc pod uwagę, że gdy te systemy były popularne, swobodny dostęp do każdej kasety byłby spełnieniem marzeń.
źródło
Wiem, że to pytanie jest trochę stare, ale chciałbym coś dodać do dyskusji. Większość odpowiedzi tutaj koncentruje się wokół emulatorów interpretujących instrukcje maszynowe emulowanych systemów.
Istnieje jednak bardzo dobrze znany wyjątek od tego zwanego „UltraHLE” ( artykuł WIKIpedia ). UltraHLE, jeden z najsłynniejszych emulatorów, jaki kiedykolwiek stworzono, emulował komercyjne gry Nintendo 64 (z przyzwoitą wydajnością na komputerach domowych) w czasach, gdy powszechnie uważano to za niemożliwe. W rzeczywistości Nintendo wciąż produkowało nowe tytuły na Nintendo 64, kiedy stworzono UltraHLE!
Po raz pierwszy widziałem artykuły o emulatorach w czasopismach drukowanych, gdzie wcześniej widziałem je tylko omawiane w Internecie.
Ideą UltraHLE było uniemożliwienie poprzez emulację wywołań biblioteki C zamiast wywołań na poziomie komputera.
źródło
Warto spojrzeć na próbę Imran Nazar napisania emulatora Gameboy w JavaScript.
źródło
Po stworzeniu własnego emulatora mikrokomputera BBC z lat 80. (wpisz VBeeb w Google), musisz wiedzieć o kilku sprawach.
Praktycznie rzecz biorąc, zazwyczaj chcesz pisać z myślą o szybkości i wierności emulacji. Wynika to z faktu, że oprogramowanie w systemie docelowym będzie (może) działać wolniej niż oryginalny sprzęt w systemie źródłowym. Może to ograniczać wybór języka programowania, kompilatorów, systemu docelowego itp.
Ponadto musisz opisać to, co jesteś przygotowany do emulacji, na przykład nie jest konieczne emulowanie stanu napięcia tranzystorów w mikroprocesorze, ale prawdopodobnie jest to konieczne do emulacji stanu zestawu rejestrów mikroprocesora.
Ogólnie mówiąc, im mniejszy poziom szczegółowości emulacji, tym większa wierność oryginalnemu systemowi.
Wreszcie informacje dotyczące starszych systemów mogą być niepełne lub mogą nie istnieć. Zdobycie oryginalnego sprzętu jest więc niezbędne, a przynajmniej wyróżnienie innego dobrego emulatora napisanego przez kogoś innego!
źródło
Tak, cały tekst binarnego kodu maszynowego należy interpretować „ręcznie”. Co więcej, przez większość czasu musisz także symulować egzotyczny sprzęt, który nie ma odpowiednika na docelowej maszynie.
Najprostszym podejściem jest interpretacja instrukcji jedna po drugiej. To działa dobrze, ale jest powolne. Szybszym podejściem jest ponowna kompilacja - tłumaczenie źródłowego kodu maszynowego na docelowy. Jest to bardziej skomplikowane, ponieważ większość instrukcji nie będzie mapować jeden na jeden. Zamiast tego będziesz musiał opracować skomplikowane obejścia, które wymagają dodatkowego kodu. Ale ostatecznie jest znacznie szybszy. Większość współczesnych emulatorów to robi.
źródło
... --- ...
- te trzy kody Morse'a reprezentują trzy litery S, O, S.” Ponieważ...
jest to kod reprezentujący literę „S”. Nie?Podczas opracowywania emulatora interpretujesz zespół procesora, nad którym pracuje system (Z80, 8080, PS CPU itp.).
Musisz także emulować wszystkie urządzenia peryferyjne, które ma system (wyjście wideo, kontroler).
Powinieneś zacząć pisać emulatory dla systemów SIMPE, takich jak stary dobry Game Boy (który używa procesora Z80, czyż nie mylę się) LUB dla C64.
źródło
Przykład tego można znaleźć na stronie http://queue.acm.org/detail.cfm?id=1755886 .
To pokaże również, dlaczego „potrzebujesz” procesora wieloczęstotliwościowego do emulacji 1 MHz.
źródło
Sprawdź także Emulators.com Darka Mihocka, gdzie znajdziesz świetne porady dotyczące optymalizacji na poziomie instrukcji dla JIT i wiele innych korzyści związanych z budowaniem wydajnych emulatorów.
źródło
Nigdy nie robiłem nic tak fantazyjnego, aby emulować konsolę do gier, ale raz poszedłem na kurs, w którym zadaniem było napisanie emulatora dla maszyny opisanej w Andrew Tanenbaums Structured Computer Organisation . To była dobra zabawa i dało mi wiele aha chwil. Możesz wziąć tę książkę przed zanurzeniem się w pisaniu prawdziwego emulatora.
źródło
Porady na temat emulacji prawdziwego systemu lub własnej rzeczy? Mogę powiedzieć, że emulatory działają poprzez emulację CAŁEGO sprzętu. Może nie w dół do obwodu (tak jak robiłoby to przesuwanie bitów jak w HW. Przeniesienie bajtu jest końcowym wynikiem, więc kopiowanie bajtu jest w porządku). Emulator jest bardzo trudny do utworzenia, ponieważ istnieje wiele hacków (jak w nietypowych efektach), problemy z synchronizacją itp., Które trzeba symulować. Jeśli jeden (wejściowy) element jest nieprawidłowy, cały system może się zepsuć lub w najlepszym razie mieć błąd / usterkę.
źródło
Device Shared Source Emulator zawiera kod źródłowy do zbudowania emulator PocketPC / Smartphone (Wymaga Visual Studio działa w systemie Windows). Pracowałem nad wersją V1 i V2 wersji binarnej.
Rozwiązuje wiele problemów związanych z emulacją: - wydajne tłumaczenie adresu z gościa wirtualnego na gościa fizycznego na gościa wirtualnego - kompilacja JIT kodu gościa - symulacja urządzeń peryferyjnych, takich jak karty sieciowe, ekran dotykowy i dźwięk - integracja interfejsu użytkownika, dla klawiatury i myszy hosta - zapisywanie / przywrócenie stanu do symulacji wznowienia z trybu niskiego poboru mocy
źródło
Aby dodać odpowiedź udzieloną przez @Cody Brocious
W kontekście wirtualizacji, w której emulujesz nowy system (CPU, I / O itp.) Na maszynie wirtualnej, możemy zobaczyć następujące kategorie emulatorów.
Interpretacja: bochs jest przykładem interpretera, jest emulatorem PC x86, bierze każdą instrukcję z systemu gościa, tłumaczy ją na inny zestaw instrukcji (hosta ISA), aby uzyskać zamierzony efekt. Tak, jest bardzo wolny, nie robi 'cache cache, więc każda instrukcja przechodzi ten sam cykl.
Dynamiczny emulator: Qemu to emulator dynamiczny. Tłumaczenie na bieżąco instrukcji gości również buforuje wyniki. Najlepsze jest to, że wykonuje jak najwięcej instrukcji bezpośrednio w systemie hosta, dzięki czemu emulacja jest szybsza. Jak wspomniała Cody, dzieli kod na bloki (1 pojedynczy przepływ wykonania).
Emulator statyczny: o ile wiem, nie ma emulatora statycznego, który mógłby być pomocny w wirtualizacji.
źródło
Jak zacznę emulację.
1. Zdobądź książki oparte na programowaniu niskiego poziomu, będziesz go potrzebować do „udawanego” systemu operacyjnego Nintendo… game boy…
2. Zdobądź książki na temat emulacji, a może rozwoju systemu operacyjnego. (nie będziesz robić OS, ale najbliżej tego.
3. spójrz na niektóre emulatory open source, szczególnie te z systemu, dla którego chcesz utworzyć emulator.
4. skopiuj fragmenty bardziej złożonego kodu do IDE / kompilatora. Pozwoli ci to zaoszczędzić na pisaniu długiego kodu. To jest to, co robię dla rozwoju systemu operacyjnego, użyj dzielnicy Linux
źródło
Napisałem artykuł o emulacji systemu Chip-8 w JavaScript .
To świetne miejsce na rozpoczęcie, ponieważ system nie jest bardzo skomplikowany, ale nadal uczysz się, jak działają kody operacyjne, stos, rejestry itp.
Niedługo będę pisać dłuższy przewodnik dla NES.
źródło