Uwaga: nadeszło kilka odpowiedzi. Zastanów się też nad poprawieniem nowszych odpowiedzi.
- Common Lisp od happy5214
- C od luser droog
- Java od NeatMonster
- JavaScript z crempp
- C od Mike C.
- C ++ od Darius Goad
- Postscript od luser droog
- C ++ od JoeFish
- JavaScript z całkowicie subiektywnego
- C z RichTX
- C ++ od Dave'a C.
- Haskell z JB
- Python z ja
8086 to pierwszy mikroprocesor Intel x86. Twoim zadaniem jest napisanie emulatora. Ponieważ jest to stosunkowo zaawansowane, chcę go ograniczyć:
- Należy zaimplementować tylko następujące kody operacyjne:
- mov, push, pop, xchg
- add, adc, sub, sbb, cmp i lub lub xor
- inc, dec
- call, ret, jmp
- jb, jz, jbe, js, jnb, jnz, jnbe, jns
- stc, clc
- hlt, nop
- W rezultacie musisz tylko obliczyć flagi carry, zero i sign
- Nie implementuj segmentów. Załóżmy
cs = ds = ss = 0
. - Brak prefiksów
- Żadnych rodzajów przerwań ani portów IO
- Brak funkcji ciągów
- Brak dwubajtowych kodów operacyjnych (0F ..)
- Brak arytmetyki zmiennoprzecinkowej
- (oczywiście) żadnych rzeczy 32-bitowych, sse, mmx, ... cokolwiek, co nie zostało jeszcze wynalezione w 1979 roku
- Nie musisz liczyć cykli ani wykonywać żadnych pomiarów czasu
Zacznij od ip = 0
i sp = 100h
.
Wejście: Twój emulator powinien pobrać program binarny w dowolnym formacie, który ci się podoba (odczyt z pliku, predefiniowana tablica, ...) i załadować go do pamięci pod adresem 0.
Wyjście: RAM wideo rozpoczyna się pod adresem 8000h, każdy bajt ma jeden znak (ASCII-). Emuluj ekran 80x25 do konsoli. Traktuj bajty zerowe jak spacje.
Przykład:
08000 2E 2E 2E 2E 2E 2E 2E 2E 2E 00 00 00 00 00 00 00 ................
08010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08050 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 00 00 Hello,.world!...
Uwaga: Jest to bardzo podobne do prawdziwego trybu wideo, który zwykle ma wartość 0xB8000 i ma inny bajt na znak dla kolorów.
Kryteria wygranej:
- Wszystkie wymienione instrukcje muszą zostać wdrożone
Zrobiłem niekomentowany program testowy ( link , źródło nasm ), który powinien działać poprawnie. Wyprowadza
......... Hello, world! 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ################################################################################ ## ## ## 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 ## ## ## ## 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 ## ## ## ## 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ################################################################################
Nie jestem pewien, czy powinien to być codegolf; to trochę trudne zadanie, więc każde zgłoszenie i tak zyska wiele pozytywnych opinii. Proszę skomentuj.
Oto kilka linków, które pomogą ci w tym zadaniu:
- format instrukcji , więcej
- tabela kodów operacyjnych
- opisy kodów operacyjnych
- 16-bitowe dekodowanie modów R / M
- rejestry , rejestr flag
- Podręcznik z 1979 r
To mój pierwszy wpis na tę platformę. Jeśli są jakieś błędy, proszę je wskazać; jeśli przegapiłem jakiś szczegół, po prostu zapytaj.
źródło
Odpowiedzi:
Zapraszam do widelca i gry w golfa: https://github.com/julienaubert/py8086
Dołączyłem również interaktywny debugger.
Istnieją trzy pliki: emu8086.py (wymagane) console.py (opcjonalne dla wyjścia wyświetlania), disasm.py (opcjonalne, aby uzyskać listę asm w codegolf).
Aby uruchomić z wyświetlaczem (uwaga używa przekleństw):
Aby uruchomić z interaktywnym debuggerem:
Aby uruchomić z nieinteraktywnym „debuggerem”:
Program „ codegolf ” powinien znajdować się w tym samym katalogu.
emu8086.py
console.py
disasm.py
Na githubie
źródło
Haskell,
256234196 liniiJuż od jakiegoś czasu pracuję nad tym, co było w toku, chciałem go jeszcze trochę dopracować przed opublikowaniem, ale teraz oficjalnie zaczęła się zabawa, nie ma już sensu ukrywać go. Zauważyłem podczas wydobywania, że ma dokładnie 256 linii, więc przypuszczam, że jest w „niezwykłym” punkcie swojego istnienia.
Co znajduje się: ledwo wystarczy zestawu instrukcji 8086, aby bezbłędnie uruchomić przykładowy plik binarny. Obsługiwany jest kod samomodyfikujący. (pobieranie wstępne: zero bajtów)
Jak na ironię, pierwsze wystarczające iteracje kodu były dłuższe i obsługiwały mniejszy zakres opcode. Refaktoryzacja okazała się korzystna zarówno pod względem długości kodu, jak i zasięgu operacji.
Co się dzieje: oczywiście segmenty, prefiksy i wielobajtowe kody, przerwania, porty I / O, operacje na łańcuchach i FP. Początkowo postępowałem zgodnie z pierwotnym
PUSH SP
zachowaniem, ale musiałem go porzucić po kilku iteracjach.Wyniki flagi przenoszenia są prawdopodobnie bardzo pomieszane w kilku przypadkach
ADC
/SBB
.W każdym razie oto kod:
Dane wyjściowe dostarczonej próbki binarnej idealnie pasują do specyfikacji. Wypróbuj za pomocą wywołania, takiego jak:
Większość niezaimplementowanych operacji spowoduje po prostu błąd dopasowania wzorca.
Nadal zamierzam wziąć pod uwagę nieco więcej i zaimplementować rzeczywiste wyjście na żywo za pomocą przekleństw.
Aktualizacja 1: zmniejszono ją do 234 linii. Lepiej uporządkuj kod według funkcjonalności, ponownie dopasuj, co mogłoby być, próbowałem trzymać się 80 kolumn. I wielokrotnie refaktoryzował ALU.
Aktualizacja 2: minęło pięć lat, pomyślałem, że aktualizacja może sprawić, że skompiluje się bezbłędnie na najnowszym GHC. Po drodze:
<$>
i<*>
w Preludium.Jak mówią komentarze do kodu, 5 wierszy (import Data.Char, 8-bitowe odwzorowania rejestrów i zrzut ekranu) są niezgodne ze specyfikacją, więc możesz je zdyskontować, jeśli masz na to ochotę :-)
źródło
.|.
? / 10charC - 7143 linii (sam procesor 3162 linii)
EDYCJA: Kompilacja systemu Windows ma teraz menu rozwijane umożliwiające zmianę dysków wirtualnych.
Napisałem pełny emulator PC 80186 / V20 (z CGA / MCGA / VGA, sound blaster, adlib, myszą itp.), Emulowanie 8086 w żaden sposób nie jest trywialne. Osiągnięcie pełnej dokładności zajęło wiele miesięcy. Oto moduł CPU tylko z mojego emulatora.
http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c
Jako pierwszy przyznam, że w tym emulatorze używam zbyt wielu zmiennych globalnych. Zacząłem pisać to, kiedy byłem całkiem nowy w C, i to pokazuje. Pewnego dnia muszę to trochę posprzątać. Większość innych plików źródłowych w nim nie wygląda tak brzydko.
Cały kod (i kilka zrzutów ekranu, jeden poniżej) można zobaczyć tutaj: http://sourceforge.net/p/fake86
Byłbym bardzo szczęśliwy, mogąc pomóc każdemu, kto chce napisać własny, ponieważ jest to świetna zabawa, a ty dużo wiesz o procesorze! Oświadczenie: Nie dodałem emulacji 8080 V20, ponieważ prawie nigdy nie była używana w programie na PC. Wydaje się, że to dużo pracy bez zysku.
źródło
Postscriptum (
130200367517531222246 linii)Wciąż w toku, aleChciałem pokazać kod, aby zachęcić innych do pokazania kodu .Zestaw rejestrów jest reprezentowany jako jeden ciąg, więc różne rejestry wielkości bajtów i słów mogą naturalnie nakładać się na siebie, odnosząc się do podłańcuchów. Podciągi są używane jako wskaźniki w całym tekście, dzięki czemu rejestr i lokalizacja pamięci (podciąg ciągu pamięci) mogą być traktowane jednolicie w funkcjach operatora.
Następnie jest garść słów do pobierania i przechowywania danych (bajtu lub słowa) z „wskaźnika”, z pamięci, z pamięci [(IP)] (zwiększanie adresu IP). Następnie istnieje kilka funkcji pobierania bajtu MOD-REG-R / M oraz ustawiania zmiennych REG i R / M i MOD oraz dekodowania ich za pomocą tabel. Następnie operator wprowadza klucz do bajtu kodu operacji. Zatem pętla wykonania jest po prostu
fetchb load exec
.Zaimplementowałem tylko garść kodów, ale gOtrzymanie dekodowania operandu wydawało się takim kamieniem milowym, że chciałem się nim podzielić.edycja: Dodano słowa do rozszerzenia liczb ujemnych. Więcej kodów. Poprawka w przypisaniach rejestru. Komentarze Nadal pracuję nad flagami i wypełnianiem operatorów. Dane wyjściowe dają pewne możliwości: wyjściowy tekst na standardowe wyjście po zakończeniu, ciągłe wyświetlanie przy użyciu kodów vt100, wyświetlanie w oknie obrazu przy użyciu czcionki CP437.
edycja: Zakończono pisanie, rozpoczęto debugowanie. Otrzymuje pierwsze cztery kropki wyniku! Wtedy przeniesienie idzie źle. Senny.
edycja: Wydaje mi się, że posortowałem flagę Carry. Część historii wydarzyła się na comp.lang.postscript . Dodałem urządzenie do debugowania, a dane wyjściowe trafiają do okna graficznego (używając mojej wcześniej napisanej czcionki Code-Page 437 Type-3 ), więc tekst może być pełen śladów i zrzutów. Pisze „Hello World!” a potem jest ta podejrzana karetka. A potem całe mnóstwo nic. :( Dotrzemy tam. Dziękujemy za całą zachętę!
edycja: uruchamia test do końca. Ostatnie kilka błędów to: XCHG robi 2 {read store}, które oczywiście kopiuje, a nie wymienia, ORAZ nie ustawia flag, (FE) INC próbuje uzyskać słowo ze wskaźnika bajtowego.
edycja: Całkowite ponowne pisanie od zera za pomocą zwięzłej tabeli z podręcznika ( zmieniłem nową stronę! ). Zaczynam myśleć, że pominięcie sklepu w opcodes było złym pomysłem, ale pomogło utrzymać optab ładnym. Tym razem brak zrzutu ekranu. Dodałem licznik instrukcji i mod-trigger, aby zrzucić pamięć wideo, dzięki czemu łatwo przeplata się z informacjami o debugowaniu.
edit: Ponownie uruchamia program testowy! Ostatnie kilka błędów krótszego przepisywania było zaniedbywaniem znakowania bezpośredniego bajtu w opcodes 83 (grupa „Natychmiastowa”) i EB (krótki JMP). 24-liniowy wzrost obejmuje dodatkowe procedury debugowania potrzebne do wyśledzenia tych ostatnich błędów.
I wyjście (z końcem skróconego wyjścia debugowania).
źródło
JavaScript
Piszę emulator 486 w javascript inspirowany przez jslinux. Gdybym wiedział, ile to będzie pracy, prawdopodobnie nigdy bym się nie rozpoczął, ale teraz chcę to zakończyć.
Potem spotkałem się z twoim wyzwaniem i byłem bardzo szczęśliwy, że mogę przetestować program 8086.
Możesz „zobaczyć”, jak działa na żywo tutaj: http://codinguncut.com/jsmachine/
Miałem jeden problem podczas drukowania bufora graficznego. Tam, gdzie powinny być spacje, pamięć zawiera elementy „00”. Czy poprawnie interpretuje się „0x00” jako spację, czy mam błąd w emulatorze?
Twoje zdrowie,
Johannes
źródło
C ++
Chciałbym przesłać nasz wpis dotyczący tego wyzwania dotyczącego kodu. Został napisany w c ++ i doskonale uruchamia program testowy. Wdrożyliśmy 90% jednobajtowych kodów operacyjnych i podstawowej segmentacji (niektóre wyłączone, ponieważ nie działa z programem testowym).
Zapis programu: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator
Możesz znaleźć kod w pliku zip na końcu posta na blogu.
Zrzut ekranu wykonujący program testowy:
Zajęło to trochę czasu ... jeśli masz jakieś pytania lub komentarze, napisz do mnie. Było to z pewnością świetne ćwiczenie w programowaniu partnerów.
źródło
ret imm
instrukcja jest niepoprawna (patrz tutaj ) i brakuje Ci0xff
grupy. Lubię jednak twoje komunikaty o błędach: rzut „Natychmiastowa wartość nie może zapisać wartości, opóźnić.”;cs
rejestrudo
Wielkie wyzwanie i moje pierwsze. Utworzyłem konto tylko dlatego, że wyzwanie bardzo mnie zaintrygowało. Wadą jest to, że nie mogłem przestać myśleć o wyzwaniu, kiedy miałem prawdziwą, płatną pracę programistyczną.
Czuję się zmuszony do uruchomienia ukończonej emulacji 8086, ale to kolejne wyzwanie ;-)
Kod jest napisany w ANSI-C, więc po prostu skompiluj / połącz pliki .c razem, przekaż plik binarny codegolf i idź.
źródło skompresowane
źródło
C ++ 1064 wierszy
Fantastyczny projekt. Zrobiłem emulator Intellivision wiele lat temu, więc wspaniale było znów napiąć mięśnie.
Po około tygodniu pracy nie mogłem być bardziej podekscytowany, gdy to się stało:
Trochę debugowania później i ... SHAZAM!
Ponadto przebudowałem oryginalny program testowy bez rozszerzeń 80386, ponieważ chciałem zbudować emulator zgodny z 8086 i nie robić żadnych dodatkowych instrukcji. Bezpośredni link do kodu tutaj: plik zip .
Ok, mam za dużo zabawy. Przerwałem zarządzanie pamięcią i ekranem, a teraz ekran aktualizuje się, gdy bufor ekranu jest zapisywany. Zrobiłem film :)
http://www.youtube.com/watch?v=qnAssaTpmnA
Aktualizacje: Rozpoczęło się pierwsze przejście do segmentacji. W rzeczywistości wdrożono bardzo niewiele instrukcji, ale przetestowałem to, przenosząc CS / DS i SS, i wszystko nadal działa dobrze.
Dodano także podstawową obsługę przerwań. Bardzo szczątkowy. Ale wdrożyłem int 21h, aby wydrukować ciąg. Dodano kilka wierszy do źródła testowego i również je przesłałem.
Jeśli ktoś ma jakiś dość prosty kod asemblujący, który przetestowałby segmenty, chciałbym się nim bawić.
Próbuję dowiedzieć się, jak daleko chcę to zrobić. Pełna emulacja procesora? Tryb VGA? Teraz piszę DOSBox.
12/6: Sprawdź, tryb VGA!
źródło
C ++ - 4455 linii
I nie, nie tylko spełniłem wymagania pytania. Zrobiłem CAŁĄ 8086, w tym 16 nigdy wcześniej nieznanych kodów. reenigne pomógł odkryć te kody.
https://github.com/Alegend45/IBM5150
źródło
#include "cpu.h"
trudno jest zobaczyć.JavaScript - 4,404 linii
Natknąłem się na ten post, szukając informacji na temat mojego emulatora. Ten post Codegolf był dla mnie absolutnie nieoceniony. Przykładowy program i związany z nim zespół umożliwiły łatwe debugowanie i sprawdzenie, co się dzieje.
Dziękuję Ci!!!
A oto pierwsza wersja mojego emulatora Javascript 8086.
Cechy:
Próbny
Mam wersję demo online, nie krępuj się z nią zagrać i daj mi znać, jeśli znajdziesz błędy :)
http://js86emu.chadrempp.com/
Aby uruchomić program codegolf
1) kliknij przycisk ustawień
2), a następnie po prostu kliknij wczytaj (możesz grać tutaj z opcjami debugowania, takimi jak przechodzenie przez program). Program codegolf jest obecnie jedynym dostępnym, pracuję nad tym, aby uzyskać więcej online.
Źródło
Pełne źródło tutaj. https://github.com/crempp/js86emu
Próbowałem wkleić tutaj treść emulacji 8086 (jak sugeruje klamka), ale przekroczyła limit znaków („Treść jest ograniczona do 30000 znaków; wpisałeś 158 272”).
Oto szybki link do kodu, który tu wkleiłem - https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/8086.js
*Edit - updated for new demo and repo location
źródło
Jawa
Tak długo chciałem stawić czoła temu wyzwaniu i w końcu poświęciłem temu czas. Do tej pory było to wspaniałe doświadczenie i z dumą mogę stwierdzić, że w końcu je ukończyłem.
Źródło
Kod źródłowy jest dostępny na GitHub pod adresem NeatMonster / Intel8086 . Próbowałem dokumentować prawie wszystko za pomocą holly 8086 Family User Manual .
Zamierzam zaimplementować wszystkie brakujące kody i funkcje, więc możesz sprawdzić wersję 1.0 dla wersji zawierającej tylko te wymagane do tego wyzwania.
Ogromne podziękowania dla @copy!
źródło
Common Lisp - 580 loc (442 bez pustych linii i komentarzy)
Wykorzystałem to wyzwanie jako wymówkę do nauki Common Lisp. Oto wynik:
Oto wynik w Emacsie:
Chcę podkreślić trzy główne cechy. Kod ten znaczący sposób korzysta z makr przy wykonywaniu instrukcji, takich jak
mov
,xchg
oraz artithmetic operacje. Każda instrukcja zawieradisasm-instr
wywołanie makra. To implementuje dezasemblację wraz z rzeczywistym kodem przy użyciu zmiennej globalnej if over ustawionej w czasie wykonywania. Jestem szczególnie dumny z podejścia agnostycznego przeznaczonego do zapisywania wartości w rejestrach i adresach pośrednich. Makra implementujące instrukcje nie dbają o miejsce docelowe, ponieważ formularze wstawiane dla każdej z opcji będą działać z ogólnymsetf
makrem Common Lisp.Kod można znaleźć na moim repozytorium GitHub . Poszukaj gałęzi „codegolf”, ponieważ już zacząłem implementować inne funkcje 8086 w master. Zaimplementowałem już flagi przepełnienia i parzystości wraz z rejestrem FLAGS.
Istnieją trzy operacje w tym, nie w 8086,
0x82
i0x83
wersjach operatorów logicznych. Zostało to złapane bardzo późno i usunięcie tych operacji byłoby dość niechlujne.Chciałbym podziękować @ja za jego wersję Pythona, która zainspirowała mnie na początku tego przedsięwzięcia.
źródło
C -
319348 liniiTo jest mniej więcej bezpośrednie tłumaczenie mojego programu Postscript na C. Oczywiście użycie stosu jest zastąpione wyraźnymi zmiennymi. Pola instrukcji są podzielone na zmienne
o
- bajt opcode instrukcji,d
- pole kierunku,w
- pole szerokości. Jeśli jest to instrukcja „mod-reg-r / m”, bajt mr-rm jest wczytywanystruct rm r
. Dekodowanie pól reg i r / m przebiega w dwóch krokach: obliczenie wskaźnika na dane i załadowanie danych, ponowne użycie tej samej zmiennej. Więc dla czegoś takiegoADD AX,BX
, najpierw x jest wskaźnikiem do ax, a y jest wskaźnikiem do bx, następnie x jest zawartością (ax), a y jest treścią (bx). Wymagane jest wiele rzutowania, aby ponownie użyć zmiennej dla różnych typów takich jak ten.Bajt opkodu jest dekodowany za pomocą tabeli wskaźników funkcji. Każda funkcja składa się z makr dla elementów wielokrotnego użytku.
DW
Makro jest obecny we wszystkich funkcjach OPCODE i dekodujed
iw
zmienne zo
bajtu rozkazu.RMP
Makro przeprowadza pierwszy etap dekodowania „MR-RM” bajtów, orazLDXY
przeprowadza się drugi etap. W kodach, które przechowują wynik, używa sięp
zmiennej, aby przytrzymać wskaźnik do lokalizacji wyniku, az
zmiennej, aby zachować wartość wyniku. Flagi są obliczane po obliczeniuz
wartości.INC
IDEC
operacje uratować flagę carry przed użyciem rodzajoweMATHFLAGS
funkcję (jako częśćADD
lubSUB
submacro) i przywróć go posłowia, aby zachować Carry.Edycja: naprawiono błędy!
Edycja: rozwinięty i skomentowany. Kiedy
trace==0
teraz wysyła komendę ANSI move-to-0,0 podczas zrzutu wideo. Więc lepiej symuluje rzeczywisty obraz. TaBIGENDIAN
rzecz (która nawet nie działała) została usunięta. W niektórych miejscach opiera się na kolejności bajtów little-endian, ale planuję to naprawić w następnej wersji. Zasadniczo cały dostęp do wskaźnika musi przejść przez funkcjeget_
i,put_
które jawnie (de) składają bajty w kolejności LE.Używanie makr na etapach różnych operacji zapewnia bardzo ścisłe dopasowanie semantyczne do sposobu działania kodu postscriptowego w czysto sekwencyjny sposób. Na przykład wszystkie cztery pierwsze kody 0x00-0x03 są instrukcjami DODAJ o zmiennym kierunku (REG -> REG / MOD, REG <- REG / MOD) i rozmiarach bajtów / słów, więc są reprezentowane dokładnie tak samo w tabeli funkcji .
Tabela funkcji jest tworzona za pomocą tego makra:
która dotyczy
OPF()
każdej reprezentacji kodu operacyjnego.OPF()
jest zdefiniowany jako:Tak więc pierwsze cztery kody są rozwijane (raz) do:
Funkcje te wyróżniają się wynikiem
DW
makra, które określa kierunek i bity bajt / słowo prosto z bajtu opcode. Po rozwinięciu treści jednej z tych funkcji (raz) powstaje:Jeżeli główna pętla już ustawiła
o
zmienną:Rozszerzanie jeszcze raz daje całe „mięso” kodu operacyjnego:
I w pełni wstępnie przetworzona funkcja, przeszła przez
indent
:Nie jest to najlepszy styl C do codziennego użytku, ale używanie makr w ten sposób wydaje się być idealne do tego, aby implementacja była tutaj bardzo krótka i bardzo bezpośrednia.
Wyjście programu testowego, z ogonem wyjścia śledzenia:
Udostępniłem kilka wcześniejszych wersji w comp.lang.c, ale nie były one bardzo zainteresowane.
źródło
indent
ed 5810 linii.