Czytam książkę The Elements of Computing Systems: Building a Modern Computer from First Principles , która zawiera projekty obejmujące budowę komputera, od bramek logicznych aż po aplikacje wysokiego poziomu (w tej kolejności). Obecny projekt, nad którym pracuję, polega na napisaniu asemblera przy użyciu wybranego przeze mnie języka wysokiego poziomu, w celu przetłumaczenia kodu asemblera hacka na kod maszynowy hacka (Hack to nazwa platformy sprzętowej zbudowanej w poprzednich rozdziałach). Chociaż cały sprzęt został zbudowany w symulatorze, próbowałem udawać, że naprawdę konstruuję każdy poziom, używając tylko narzędzi dostępnych mi w tym momencie w prawdziwym procesie.
To powiedziawszy, skłoniło mnie do myślenia. Używanie języka wysokiego poziomu do pisania mojego asemblera jest z pewnością wygodne, ale czy w przypadku pierwszego asemblera, jaki kiedykolwiek napisano (tj. W historii), nie trzeba go pisać w kodzie maszynowym, ponieważ to wszystko wtedy istniało?
I skorelowane pytanie ... a może dzisiaj? Jeśli pojawi się zupełnie nowa architektura procesora, nowy zestaw instrukcji i zupełnie nowa składnia asemblera, to w jaki sposób asembler zostanie zbudowany? Zakładam, że nadal możesz używać istniejącego języka wysokiego poziomu do generowania plików binarnych dla programu asemblera, ponieważ jeśli znasz składnię zarówno języka asemblera, jak i języka maszynowego dla nowej platformy, to zadanie pisania asemblera jest naprawdę zadanie analizy tekstu i nie jest nieodłącznie związane z tą platformą (tj. musi być napisane w języku maszynowym tej platformy) ... właśnie z tego powodu jestem w stanie „oszukać” pisząc mój asembler Hack w 2012 roku i skorzystać z wcześniej istniejącej język wysokiego poziomu, który mi pomoże.
Odpowiedzi:
Niekoniecznie. Oczywiście pierwsza wersja a0.00 asemblera musiała zostać napisana w kodzie maszynowym, ale nie byłaby wystarczająco mocna, aby nazywać się asemblerem. Nie obsługiwałby nawet połowy cech „prawdziwego” asemblera, ale wystarczyłoby napisać kolejną wersję samego siebie. Następnie możesz ponownie napisać v0.00 w podzbiorze języka asemblera, nazwać go v0.01, użyć go do zbudowania następnego zestawu funkcji twojego asemblera v0.02, a następnie użyć v0.02 do zbudowania v0.03, i i tak dalej, aż dojdziesz do wersji 1.00. W rezultacie tylko pierwsza wersja będzie w kodzie maszynowym; pierwsza wydana wersja będzie w języku asemblera.
Za pomocą tej sztuczki mam bootstrapped rozwój kompilatora w języku szablonów. Moja początkowa wersja używała
printf
instrukcji, ale pierwsza wersja, której użyłem w mojej firmie, używała właśnie przetwarzanego szablonu. Faza ładowania trwała mniej niż cztery godziny: jak tylko mój procesor mógł wygenerować ledwie przydatne wyjście, ponownie napisałem go w swoim własnym języku, skompilowałem i wyrzuciłem wersję bez szablonów.źródło
diff
z bieżącym generatorem kodu, aby zobaczyć, że wygenerowana część kodu nie zmieniła się w nieoczekiwany sposób, zastępując kod na miejscu, oraz uruchom go jeszcze raz, aby zakończyć cykl.Według Wikipedii, pierwszy w historii asembler / asembler został wdrożony dla IBM 701 przez Nathaniela Rochestera . (Daty są trochę niepewne w artykule w Wikipedii. Stwierdzono, że Rochester dołączył do IBM w 1948 r., Ale inna strona Wikipedii stwierdza, że 701 został publicznie ogłoszony w 1952 r. A ta strona IBM stwierdza, że „[rzeczywisty] projekt rozpoczął się w lutym 1, 1951 r., A ukończono rok później ” .)
Jednak „Asemblery i ładowarki” Davida Salomona stwierdza (na stronie 7), że EDSAC miał również asembler:
Zakładając, że akceptujemy, że „Pierwsze zamówienia” mają pierwszeństwo, mamy wyraźne dowody, że pierwszy asembler został zaimplementowany w kodzie maszynowym.
Ten wzorzec (zapisywanie początkowych asemblerów w kodzie maszynowym) byłby normą już w latach pięćdziesiątych. Jednak według Wikipedii „[a] asemblery były pierwszymi narzędziami językowymi, które same się ładowały”. Zobacz także ten rozdział, w którym wyjaśniono, w jaki sposób napisano kod maszynowy pierwotnego asemblera, aby załadować bardziej zaawansowany asembler zakodowany w języku asemblera.
Obecnie asemblery i kompilatory są pisane w językach wyższego poziomu, a asembler lub kompilator dla nowej architektury maszyn jest zwykle rozwijany w innej architekturze i kompilowany krzyżowo.
(FWIW - pisanie i debugowanie nietrywialnych programów w kodzie maszynowym jest niezwykle pracochłonnym procesem. Ktoś, kto opracowuje asembler w kodzie maszynowym, najprawdopodobniej jak najszybciej uruchomi asembler do asemblera napisanego w asemblerze).
Ta strona Wikipedii na temat kompilatorów i asemblerów ładowania jest warta przeczytania ... jeśli to wszystko jest dla ciebie kłopotliwe.
źródło
Zakładam, że pierwsze asemblery zostały napisane w kodzie maszynowym, ponieważ, jak mówisz, nic innego nie było wtedy dostępne.
Dziś jednak, gdy pojawia się zupełnie nowa architektura procesora, używamy tak zwanego kompilatora krzyżowego , który jest kompilatorem, który wytwarza kod maszynowy nie dla architektury, na której jest uruchomiony, ale dla innej architektury.
(W rzeczywistości, jak jestem pewien, dowiesz się później w czytanej książce, nie ma absolutnie nic, co czyni kompilator z natury bardziej odpowiednim do tworzenia kodu maszynowego dla architektury, na której działa), inna architektura. To tylko kwestia architektury, którą Ty, jako twórca kompilatora, zamierzasz celować.)
Tak więc dzisiaj jest nawet możliwe (przynajmniej teoretycznie) stworzenie zupełnie nowej architektury i natywna kompilacja językowa na nim uruchomiona (skompilowana na innych architekturach przy użyciu kompilatorów krzyżowych), zanim jeszcze będzie asembler dla tej architektury.
źródło
Najpierw „papier” został napisany na papierze, a następnie ręcznie „skompilowany” na karty perforowane.
Mój dziadek pracował z ZRA1 (przepraszam, strona istnieje tylko w języku niemieckim, ale tłumaczenie Google jest odpowiednie do tego stopnia, że można właściwie odczytać najważniejsze fakty: D).
Modus operandi polegał na zapisaniu kodu na papierze w pewnym języku asemblera, a sekretarka faktycznie dokonała transkrypcji, aby wybić karty, a następnie przekazać je operatorowi, a wynik zostanie przekazany następnego dnia rano.
Wszystko to było w zasadzie zanim programiści mieli luksus wprowadzania danych za pomocą klawiatury i wyświetlania ich na ekranie.
źródło
Trudno mieć pewność, o bardzo pierwszego asemblera (trudno nawet określić, co to było). Wiele lat temu, kiedy napisałem kilka asemblerów dla maszyn, które nie miały asemblerów, nadal pisałem kod w języku asemblera. Następnie, kiedy miałem dość kompletną sekcję kodu, ręcznie przetłumaczyłem ją na kod maszynowy. Były to jednak dwie całkowicie odrębne fazy - kiedy pisałem kod, w ogóle nie pracowałem ani nie myślałem na poziomie kodu maszynowego.
Powinienem dodać, że w kilku przypadkach poszedłem o krok dalej: napisałem większość kodu w asemblerze, który uważałem za prostszy w użyciu, a potem napisałem małe jądro (mniej więcej to, co teraz nazywamy maszyną wirtualną) interpretować to na docelowym procesorze. To było śmiertelnie powolne (szczególnie na 8-bitowym procesorze 1 MHz), ale to nie miało większego znaczenia, ponieważ normalnie działało tylko raz (lub co najwyżej kilka razy).
źródło
Nie potrzebujesz asemblera do ręcznego złożenia kodu języka asemblera w kod maszynowy. Tak jak nie potrzebujesz edytora do pisania kodu języka asemblera.
Perspektywa historyczna
Pierwsze asemblery zostały prawdopodobnie napisane w języku asemblera, a następnie ręcznie zmontowane w kodzie maszynowym. Nawet jeśli procesor nie miał oficjalnego „języka asemblera”, programiści prawdopodobnie wykonali większość pracy przy programowaniu przy użyciu pewnego rodzaju pseudo kodu przed przetłumaczeniem tego kodu na instrukcje maszynowe.
Nawet w pierwszych dniach komputerów programiści pisali programy w formie symbolicznej notacji i tłumaczyli je na kod maszynowy przed wprowadzeniem go do komputera. W przypadku Augusta Ada King, miałaby musiał przełożyć je na karty perforowane dla Babbage „s Analytical silnika , ale niestety nigdy nie został zbudowany.
Osobiste doświadczenie
Pierwszym moim komputerem był Sinclair ZX81 (Timex 1000 w USA). Z tyłu instrukcji miał wszystkie informacje potrzebne do przetłumaczenia asemblera Z80 do kodu maszynowego (nawet opartemu wszystkich dziwnych trybie indeksu opcodes Z80 mieli).
Napisałbym program (na papierze) w języku asemblera i przebiegał na sucho przez kod. Kiedy byłem szczęśliwy, że mój program jest wolny od błędów, sprawdzałem każdą instrukcję z tyłu instrukcji, tłumaczyłem ją na kod maszynowy i zapisywałem kod maszynowy również na papierze. Na koniec wpisałbym wszystkie instrukcje kodu maszynowego do mojego ZX81 przed zapisaniem go na taśmie i próbą uruchomienia.
Gdyby to nie zadziałało, sprawdziłbym dokładnie mój zestaw ręczny, a jeśli jakiekolwiek tłumaczenie byłoby niepoprawne, załatałem bajty załadowane z taśmy przed ponownym zapisaniem i ponownym uruchomieniem programu.
Z doświadczenia mogę powiedzieć, że o wiele łatwiej debugować kod, jeśli jest napisany w języku asemblera niż w kodzie maszynowym - stąd popularność deasemblerów. Nawet jeśli nie masz asemblera, asemblowanie ręczne jest mniej podatne na błędy niż próba bezpośredniego pisania kodu maszynowego, choć myślę, że prawdziwy programista taki jak Mel może się nie zgodzić. * 8 ')
źródło
Nie ma różnicy wtedy ani teraz. Chcesz wymyślić nowy język programowania, wybierz jeden z dostępnych dziś języków, aby stworzyć pierwszy kompilator. przez pewien czas, jeśli jest to celem projektu, tworzysz kompilator w tym języku, a następnie może on sam się hostować.
Jeśli wszystko, co miałeś, to ołówek i papier oraz niektóre przełączniki lub karty dziurkowania jako interfejs użytkownika do pierwszego lub następnego nowego zestawu instrukcji, użyłeś jednego lub wszystkich dostępnych przedmiotów. Równie dobrze mógłbyś napisać język asemblera na papierze, a następnie użyć asemblera, aby przekonwertować go na kod maszynowy, może ósemkowy, a następnie w pewnym momencie, który przeszedł do interfejsu maszyny.
Kiedy dzisiaj opracowywany jest zupełnie nowy zestaw instrukcji, nie inaczej, w zależności od firmy / osób, praktyk itp. Jest całkiem prawdopodobne, że inżynier sprzętu prawdopodobnie programuje w verilog lub vhdl, pisze kilka pierwszych programów testowych ręcznie w kodzie maszynowym (prawdopodobnie w systemie szesnastkowym lub binarnym). w zależności od postępów zespołów programowych mogą bardzo szybko lub nie przestawać na język asemblera, a następnie na kompilator.
Pierwsze maszyny obliczeniowe nie były maszynami ogólnego przeznaczenia, których można użyć do tworzenia asemblerów i kompilatorów. Zaprogramowałeś je, przenosząc niektóre przewody między wyjściem wcześniejszego alu a wejściem następnego. W końcu miałeś procesor ogólnego przeznaczenia, taki, że możesz napisać asembler w asemblerze, ręcznie złożyć go, podać jako kod maszynowy, a następnie użyć go do parsowania ebcdic, ascii itp., A następnie sam-hosta. przechowuj plik binarny na niektórych nośnikach, które możesz później odczytać / załadować bez konieczności przełączania przełączników na kod maszynowy podawania ręcznego.
Pomyśl o kartach dziurkowanych i taśmie papierowej. Zamiast przełączania przełączników można z całą pewnością stworzyć całkowicie mechaniczną maszynę, urządzenie oszczędzające pracę, które stworzyło nośnik, który czytałby komputer. Zamiast wprowadzać bity kodu maszynowego za pomocą przełączników takich jak altair, można zamiast tego podawać papierową taśmę lub karty dziurkowane (używając czegoś mechanicznego, nie napędzanego procesorem, który zasila pamięć lub procesor, LUB używając małego programu ładującego napisanego kodem maszynowym). Nie był to zły pomysł, ponieważ można było stworzyć coś, napędzanego przez komputer, który mógłby również mechanicznie wytwarzać taśmy papierowe lub karty dziurkowane, a następnie przesyłać je z powrotem. Dwa źródła kart dziurkowanych, nieskomputerowe mechaniczne urządzenie oszczędzające pracę oraz maszynę sterowaną komputerowo. oba tworzą „pliki binarne” dla komputera.
źródło
W zoo komputerowym Brook'a jest jeden lub dwa przypadki, w których powiedział coś w rodzaju „mnemoniki są naszym wynalazkiem, projektant po prostu użył numerycznego kodu lub znaku, którego kod był kodem”, więc tam, gdzie maszyny nie były nawet język asemblera.
Wprowadzanie programów kończy debugowanie na panelu przednim (dla tych, którzy tego nie zrobili, był to sposób na skonfigurowanie pamięci, ustawiłeś niektóre przełączniki na adres, niektóre na wartość i nacisnąłeś przycisk lub inny przycisk, aby odczytać wartość) było powszechne znacznie później. Niektóre stare liczniki chwalą się, że nadal będą w stanie wprowadzić kod startowy dla maszyn, z których często korzystają.
Trudność bezpośredniego pisania kodu maszynowego i czytania programów ze zrzutu pamięci jest dość zależna od języka maszynowego, niektóre z nich są stosunkowo łatwe (najtrudniejsze jest śledzenie adresów), x86 jest jednym z gorszych.
źródło
Zbudowałem komputer w 1975 roku. Był bardzo zaawansowany w stosunku do swojego współczesnego Altaira, ponieważ miał „monitor ROM”, który pozwalał mi wchodzić do programów, wpisując kod maszynowy w postaci szesnastkowej i przeglądając ten kod na monitorze wideo, podobnie jak w przypadku Altair każda instrukcja maszyny musiała być wprowadzana nieco za pomocą rzędu przełączników.
Więc tak, na początku komputerów, a potem na początku komputerów osobistych ludzie pisali aplikacje w kodzie maszynowym.
źródło
Anegdota:
Kiedy nauczyłem się języka asemblera na Apple] [, w ROMie znajdował się program o nazwie mikro-asembler. Dokonał natychmiastowego tłumaczenia instrukcji asemblera na bajty, gdy je wprowadziłeś. Oznacza to, że nie było żadnych etykiet - jeśli chcesz przeskoczyć lub załadować, sam musisz obliczyć przesunięcia. Było to o wiele łatwiejsze niż przeglądanie układów instrukcji i wprowadzanie wartości szesnastkowych.
Bez wątpienia prawdziwe asemblery zostały najpierw napisane przy użyciu mikro-asemblera lub innego, niezupełnie kompletnego środowiska.
źródło