Chcę zbudować maszynę wirtualną jako niezależny od platformy sposób uruchamiania kodu gry (w zasadzie skryptów).
Maszyny wirtualne, które znam w grach, są raczej stare: Z-Machine Infocom , SCUMM LucasArtsa , Quake 3 id Software . Jako programista .net znam CLR i zapoznałem się z instrukcjami CIL, aby uzyskać przegląd tego, co faktycznie wdrażasz na poziomie maszyny wirtualnej (w porównaniu do poziomu języka). W ubiegłym roku trochę się też bawiłem w asemblerze 6502 .
Chodzi o to, że teraz, gdy chcę wdrożyć jedną, muszę głębiej kopnąć. Wiem, że istnieją maszyny wirtualne oparte na stosie i oparte na rejestrach, ale tak naprawdę nie wiem, która z nich jest lepsza w czym i czy jest więcej lub podejście hybrydowe. Muszę poradzić sobie z zarządzaniem pamięcią, zdecydować, które typy niskiego poziomu są częścią maszyny wirtualnej i muszę zrozumieć, dlaczego rzeczy takie jak ldstr działają w ten sposób.
Moją jedyną książką referencyjną (oprócz elementów Z-Machine) jest CLI Annotated Standard , ale zastanawiam się, czy jest lepszy, bardziej ogólny / podstawowy wykład dla maszyn wirtualnych? Zasadniczo coś w rodzaju Dragon Book , ale dla maszyn wirtualnych? Zdaję sobie sprawę z sztuki programowania komputerowego Donalda Knutha, która wykorzystuje maszynę wirtualną opartą na rejestrach, ale nie jestem pewien, w jaki sposób ta seria wciąż ma zastosowanie, zwłaszcza, że wciąż jest niedokończona?
Wyjaśnienie: Celem jest zbudowanie specjalistycznej maszyny wirtualnej. Na przykład Z-Machine Infocom zawiera kody OpC do ustawiania koloru tła lub odtwarzania dźwięku. Muszę więc dowiedzieć się, ile idzie na maszynę wirtualną jako OpCodes vs. kompilator, który pobiera skrypt (język TBD) i generuje z niego kod bajtowy, ale w tym celu muszę zrozumieć, co naprawdę robię.
¹ Wiem, że nowoczesna technologia pozwoliłaby mi na bieżąco interpretować język skryptowy wysokiego poziomu. Ale gdzie jest w tym zabawa? :) Google jest również trochę trudny, ponieważ maszyny wirtualne są obecnie często kojarzone z wirtualizacją systemu operacyjnego VMWare ...
źródło
do { switch(opcode) {case OP1: ... case OP2: ...} while (nextop);
a może może kompilator ... i wtedy zaczyna się zabawa - optymalizacja, aby rzeczywiście działałaQuake 3
wirtualna maszyna?Odpowiedzi:
Zacznę od sprawdzenia Lua . Zarówno jako przykładowa implementacja, jak i bardzo użyteczna maszyna wirtualna / język po wyjęciu z pudełka, jeśli w końcu zdecydujesz się nie rzucać własnym.
Kod źródłowy jest bardzo czytelny, a także kod źródłowy z adnotacjami . I niektóre dokumenty projektowe napisane przez głównego autora, Roberto Ierusalimschy.
Wreszcie, jeśli zdecydujesz się użyć go zamiast własnego, przekonasz się, że od dawna jest on ulubieńcem twórców gier i istnieje bardzo wydajna implementacja JIT .
Jeśli chodzi o stosy vs rejestry, myślę, że maszyny wirtualne oparte na stosie są łatwiejsze do zaprojektowania, ale kompilator może być bardziej złożony. Jak zauważa papier Iesualimschy, Lua była jedną z pierwszych maszyn wirtualnych w języku opartym na rejestrach, ale później pojawiło się kilka innych, w szczególności LLVM, Dalvik i niektóre nowoczesne maszyny wirtualne JavaScript.
źródło
W tej chwili nie mam żadnych konkretnych zasobów, z którymi mogę cię połączyć, ale w przeszłości badałem podobny temat i odkryłem, że VM Smalltalk jest również dobrą pomocą do nauki. Istnieje wiele prac naukowych i artykułów o kodach bajtów używanych przez Smalltalk, a także pisanie interpretatorów i maszyn wirtualnych do korzystania z tego kodu bajtowego. Wyszukiwarka Google powinna
smalltalk vm implementation
lubsmalltalk bytecode interpreter
powinna dostarczyć dużo materiałów do czytania.Jeśli chcesz zobaczyć kod źródłowy lub wypróbować implementację, polecam wersje Squeak lub Pharo.
Powiązany język / VM Self może również Cię zainteresować, ponieważ Self jest w zasadzie Smalltalk z obiektami opartymi na prototypach (podobnym do JavaScript).
źródło
Zacznę od analizy, w jaki sposób kod źródłowy [skrypt] dostaje się do komputera lub środowiska wykonawczego.
Jeśli masz coś takiego jak w dokumentach HTML
<a onclick="dosomething();">
, będziesz potrzebować bardzo szybkiego kompilatora, prędkość wykonania kodu bajtowego nie ma w tym przypadku tak wielkiego znaczenia. Jeśli twoje przypadki użycia są bliższe Java / .NET, gdzie możesz sobie pozwolić na pełną kompilację, wówczas architektura VM i struktura kodu bajtowego będą bliższe kodom bajtowym Java lub IL.Kolejnym kryterium jest to, co nazywam „kleistością”. Oryginalnie skrypty opracowano jako języki kleju - skrypty po prostu określają sposób łączenia różnych funkcji natywnych (Perl, Python, Ruby, JS). W takim przypadku efektywność VM i kodu bajtowego jest znacznie mniej istotna niż w przypadku Java / .NET, gdy większość kodu to funkcje napisane w samym języku.
Ostatnim ważnym kryterium, którego bym użył, jest rozszerzalność twojego języka. Jeśli planujesz dodać do swojego środowiska wykonawczego wiele rodzimych obiektów / funkcji zaimplementowanych, powiedzmy, w C ++, twoja architektura VM powinna być „wygodna” do integracji z C ++. Na przykład: jeśli planujesz wystawić na działanie skryptów obiekty C ++, ponieważ są one jedyną opcją dla ciebie, liczenie referencji jako zarządzanie stertami (jak Python, zobacz boost :: python jako przykład integracji). Jeśli planujesz użyć ruchomej / zagęszczającej sterty / GC, będzie to inna historia. Sposób dodawania natywnych rzeczy przez Luę w środowisku wykonawczym jest nieco trudny [dla programistów C ++].
Innymi słowy, spróbuj najpierw zdefiniować typowy przypadek użycia, a łatwiej będzie zasugerować, co dla ciebie przeczytać.
źródło