2013-04-13 11 views
9

Niedawno próbowałem zanurzyć się w świecie programowania zespołów z ostatecznym celem tworzenia własnego języka programowania. Chcę, aby mój pierwszy prawdziwy projekt był prostym asemblerem napisanym w języku C, który będzie w stanie skompletować bardzo małą część języka maszynowego x86 i utworzyć plik wykonywalny systemu Windows. Bez makr, bez łączników. Po prostu montaż.Chcę utworzyć prosty asembler w C. Gdzie powinienem zacząć?

Na papierze wydaje się dość proste. Dołącza się kod instalacyjny, wychodzi kod maszynowy.

Ale gdy tylko pomyślę o wszystkich szczegółach, nagle staje się on bardzo zniechęcający. Jakie konwencje wymaga system operacyjny? Jak wyrównać dane i obliczyć skoki? Jak wygląda wnętrze pliku wykonywalnego?

Czuję się zagubiony. Nie ma żadnych tutoriali na ten temat, które mogłem znaleźć i patrząc na kod źródłowy popularnych asemblerów nie był inspirujący (jestem gotów spróbować jeszcze raz).

Gdzie mam przejść? Jak byś to zrobił? Czy są jakieś dobre tutoriale lub literatura na ten temat?

+1

Coś, o czym warto również pomyśleć: Finite Automata, aby sprawdzić, czy użytkownik używa nawet odpowiednich instrukcji, a także będzie potrzebny parser, aby upewnić się, że to, co pisze programista, jest poprawne. Chociaż istnieje wiele rzeczy po stronie systemu, które będą musiały się martwić, istnieje również wiele teorii obliczeń, które również trzeba znać. –

+1

Może powinieneś studiować pakiet taki jak [NASM] (http://www.nasm.us/). –

+0

Sprawdź [to wyzwanie w golfa kodowym] (http://codegolf.stackexchange.com/questions/4732/emulate-an-intel-8086-cpu) dla zasobów 8086 i niezbyt krótkiego przykładowego programu z podzbiorem 8086 zarówno w formie źródłowej, jak i binarnej. IMO 1979 Manual to miejsce, od którego należy zacząć. ... Spójrz również na [moje pytanie na temat wiki zasobów montażowych] (http://stackoverflow.com/a/7203667/), w szczególności plik "PDP-1_Macro.pdf", który zawiera szczegółowy opis bardzo prymitywnego asemblera . –

Odpowiedz

3

To, czego szukasz, nie jest tutorialem ani kodem źródłowym, jest to specyfikacja :. Zobacz http://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx

Po zapoznaniu się ze specyfikacją pliku wykonywalnego, napisz program do wygenerowania. Wykonany plik wykonywalny powinien być tak prosty, jak to tylko możliwe. Po opanowaniu tego, , następnie możesz napisać prosty parser zorientowany na linię, który odczytuje nazwy instrukcji i argumenty numeryczne, aby wygenerować blok kodu do podłączenia do exe. Później możesz dodać symbole, gałęzie, sekcje, cokolwiek chcesz, i wtedy pojawi się coś takiego, jak http://www.davidsalomon.name/assem.advertis/asl.pdf.

P.S. Carl Norum ma dobry punkt w powyższym komentarzu. Jeśli twoim celem jest stworzenie własnego języka programowania, nauka pisania asemblera jest nieistotna i nie jest dobrym sposobem na rozpoczęcie (chyba że język, który chcesz utworzyć, jest językiem asemblera). Istnieją już asemblery, które produkują pliki wykonywalne ze źródła asemblera, więc twój kompilator może produkować źródło asemblera i możesz uniknąć pracy odtwarzania asemblera ... i powinieneś. Lub możesz użyć czegoś takiego jak LLVM, który rozwiąże wiele innych trudnych problemów związanych z budową kompilatora. Szanse są bardzo małe, że kiedykolwiek stworzysz własny język programowania, ale są one o wiele mniejsze, jeśli zaczniesz od zera i nie ma takiej potrzeby. Zdecyduj, jaki jest twój cel i użyj najlepszych dostępnych narzędzi, aby to osiągnąć.

4

Powinieneś spojrzeć na LLVM, llvm to modułowy backend kompilatora, najpopularniejszym front-endem jest Clang do kompilowania C/C++/Objective-C. Dobrą rzeczą w LLVM jest to, że możesz wybrać część łańcucha kompilatorów, która cię interesuje i skupić się na tym, ignorując wszystkie pozostałe. Chcesz stworzyć własny język, napisać parser, który generuje wewnętrzny kod reprezentacji LLVM, a za darmo otrzymasz wszystkie niezależne optymalizacje celów warstwy pośredniej i kompilacji do wielu różnych celów. Interesując się kompilatorem dla jakiegoś egzotycznego procesora, napisz kompilator, który pobiera kod pośredniczący LLVM i generuje twoje złożenie. Masz kilka pomysłów na temat technik optymalizacji, może na przykład automatycznego wątkowania, napisać środkową warstwę, która przetwarza pośredni kod LLVM. LLVM to zbiór bibliotek, a nie samodzielny plik binarny, taki jak GCC, a więc jest bardzo łatwy w użyciu we własnych projektach.

11

Napisałem kilka osób (asemblerów i dezasemblerów) i nie zacznę od x86. Jeśli znasz x86 lub jakikolwiek inny zestaw instrukcji, możesz pobrać i nauczyć się składni innego zestawu instrukcji w krótkim czasie (wieczór/popołudnie), przynajmniej jego udział w lwach.Czynność pisania asemblera (lub deasemblera) zdecydowanie nauczy cię zestawu instrukcji, szybko i będziesz wiedział, że ta instrukcja jest lepsza niż wielu doświadczonych programistów zestawów dla tego zestawu instrukcji, którzy nie zbadali mikrokodu na tym poziomie. msp430, pdp11 i kciuk (nie rozszerzenia thumb2) (lub mips lub openrisc) to dobre miejsce na rozpoczęcie, nie wiele instrukcji, niezbyt skomplikowane, itp.

Polecam najpierw dezasembler, a za nim zestaw instrukcji o ustalonej długości, taki jak ramię lub kciuk, czy mipy lub openrisc, itd. Jeśli nie, to przynajmniej użyj dezasemblera (zdecydowanie wybierz zestaw instrukcji, dla którego masz już asembler, linker i disasemsembler), a ołówkiem i papierem zrozumiesz związek między kodem maszynowym a złożeniem, w szczególności gałęzie, zwykle mają jedno lub więcej dziwactw, jak licznik programu jest instrukcją lub dwa naprzód po dodaniu przesunięcia, aby uzyskać inny bit, który czasami mierzy w całych instrukcjach, a nie bajtach.

Bardzo łatwo jest brutalnie zmatrzyć tekst za pomocą programu C, aby przeczytać instrukcje. Cięższym zadaniem, ale może także edukacyjnym, byłoby użycie bison/flex i nauczenie się tego języka programowania, aby umożliwić tym narzędziom tworzenie (jeszcze bardziej ekstremalnej brutalnej siły) parsera, który następnie łączy się z twoim kodem, by powiedzieć ci, co było znalezione.

Sam asembler jest dość prosty, wystarczy odczytać ascii i ustawić bity w kodzie maszynowym. Oddziały i inne instrukcje dotyczące komputera są trochę bardziej bolesne, ponieważ mogą wykonać wiele przejść przez źródło/tabele, aby całkowicie rozwiązać problem.

mov r0,r1 
    mov r2 ,#1 

asembler zaczyna analizowania tekstu do linii (zdefiniowanych jako bajtów, które następują powrót karetki 0xD lub przewód doprowadzający 0xA), usunąć białe znaki (spacje i zakładek), aż dojdziesz do czegoś spoza białej spacja, a następnie strncmp to ze znanymi mnemonikami. jeśli uderzysz, przeanalizujesz możliwe kombinacje tej instrukcji, w prostym przypadku powyżej po przejściu mov przez białe spacje do nie-białej przestrzeni, być może pierwszą rzeczą, którą znajdziesz, musi być rejestr, następnie opcjonalna biała przestrzeń, a następnie przecinek. usuń białe znaki i przecinki i porównaj je z tabelą ciągów lub po prostu przeanalizuj ją. Gdy rejestr zostanie zakończony, przejdź obok miejsca, w którym znajduje się przecinek, i powiedz, że jest to inny rejestr lub natychmiastowy. Jeśli natychmiast powiemy, że musi mieć znak #, jeśli rejestr pozwala powiedzieć, że musi rozpoczynać się od małej lub dużej litery "r". po przeanalizowaniu tego rejestru lub natychmiast, upewnij się, że nie ma nic na linii, która nie powinna znajdować się na linii. zbuduj kod maszynowy dla tej instrukcji lub przynajmniej tyle, ile możesz i przejdź do następnej linii. Może to być uciążliwe, ale nie jest trudno przetworzyć dane ...

co najmniej potrzebujesz tabeli/tablicy, która gromadzi kod maszynowy/dane podczas ich tworzenia, oraz pewną metodę oznaczania instrukcji jako niekompletną , instrukcje dotyczące komputera, które należy ukończyć w przyszłym przebiegu. będziesz również chciał tabeli/tablicy, która zbiera znalezione etykiety i adres/offset w tabeli kodu maszynowego, gdzie został znaleziony. Oprócz etykiet użytych w instrukcji jako miejsca docelowego/źródła oraz przesunięcia w tabeli/macierzy zawierającej częściowo kompletne instrukcje, które zawierają. po pierwszym przejściu przejdź przez te tabele, aż dopasujesz wszystkie definicje etykiet z etykietami używanymi jako źródło lub miejsce docelowe, używając adresu definicji/odsunięcia etykiety, aby obliczyć odległość do danej instrukcji, a następnie dokończ tworzenie kod maszynowy dla tej instrukcji. (Może być wymagany jakiś demontaż i/lub inna metoda zapamiętania, jakiego rodzaju kodowania było, kiedy wrócisz do niego później, aby zakończyć tworzenie kodu maszynowego).

Następnym krokiem jest umożliwienie wielu plików źródłowych, jeśli jest to coś, na co chcesz zezwolić. Teraz musisz mieć etykiety, które nie zostaną rozwiązane przez asemblera, więc musisz zostawić symbole zastępcze na wyjściu i zrobić jakiś smak najdłuższego skoku/instrukcji rozgałęzienia, ponieważ nie wiesz, jak daleko będzie cel podróży, spodziewaj się gorzej.Następnie istnieje format pliku wyjściowego, który wybierzesz do utworzenia/użycia, a następnie linker, który jest w większości prosty, ale musisz pamiętać, aby wpisać kod maszynowy dla końcowych instrukcji względnych, nie trudniejszych niż był w asemblerze samo.

Uwaga, pisanie asemblera niekoniecznie jest związane z tworzeniem języka programowania, a następnie pisaniem dla niego kompilatora, osobną rzeczą, różnymi problemami. Właściwie, jeśli chcesz stworzyć nowy język programowania, po prostu użyj istniejącego asemblera dla istniejącego zestawu instrukcji. Oczywiście nie jest to wymagane, ale większość instrukcji i tutoriali będzie używać podejścia bison/flex dla języków programowania, a istnieje wiele wykładów i materiałów szkoleniowych dla początkujących klas kompilacji, których można użyć, aby zacząć, a następnie zmodyfikować skrypt, aby dodać funkcje Twojego języka. Środkowe i tylne końce stanowią większe wyzwanie niż front. istnieje wiele książek na ten temat i wiele zasobów internetowych. Jak wspomniano w innej odpowiedzi, llvm nie jest złym miejscem do stworzenia nowego języka programowania, w którym średnie i backendy są dla ciebie zrobione, wystarczy skupić się na samym języku programowania, na froncie.

Powiązane problemy