Mainstream

tryby rzeczywistości 2/9 #Start klocka;

//wróć do spisu treści; tekst jest fragmentem większej całości;

#Start klocka;

Zacznijmy od tego, że tym, którym zdarza się komputer wyłączyć zdarza się go czasem włączać. Architektura x86 jest starsza ode mnie i nabrała po drodze wielu rozszerzeń. Czy była najlepszą – jedni rabini mówią to, a inni tamto. Jest jak jest. Powszechnie jednak przyjęło się pod strzechami, a reszta niejako się dostosowała poszerzając listę instrukcji. W momencie kiedy odpalacie kompa to w małych kabelkach zaczyna płynąć prąd. I do momentu aż nie wyłączycie prądu nic innego nie będzie się tam działo – będą splątane kabelki i labirynty, którymi potencjał się wyrówna, ponieważ gdzie indziej rzeczywistość stawia mu opór. Tych kabelków jest po prostu dużo i ludzie, z jakiegoś tam ewolucyjnego powodu lubią rzeczy nazywać, opisywać i definiować. Na przykład dużo cegieł lubią nazywać domem, ale czasem nazywają składem materiałów budowlanych i to chyba nie od liczności cegieł zależy czy to będzie dom czy nie. Równie trudno jest mi rozgraniczyć grupę takich obiektów i miasto, choć ponoć dla wszystkich to oczywiste. Przyjmuję, że jak jest bardzo dużo materiałów budowlanych to niech to będzie miasto. Ludzie mają wady ukryte. Dlatego zajmuję się czymś innym. !ludzie.

Włączamy prąd, przetwornik zaczyna generować pulsy prądu stałego. Dość szybkie. Budzi w ustrojstwie taki kawałek procka, który może sobie chodzi tak jak jest. To maszyna trybu rzeczywistego. Adresy pamięci odnoszą się fizycznie i wprost przełączeniem po kablach (zawsze, ale tu nie ma innej opcji, bo dopiero włączyliśmy) więc jak sobie policzymy ścieżki na magistrali (na tej pierwszej jest ich 20, a 2 stany do 20 daje nam coś ponad milion kombinacji, dlatego jest właśnie tyle czyli milion z groszami bajtów pamięci) to instrukcja dostępu do pamięci wprost posługuje się tymi ścieżkami. Takich abstraktów jak stronicowanie czy ochrona zasobów nie ma. W rejestr trafia sygnał (16 bit) z kabla 0xFFFF0 ponieważ wartości domyślne AX, BX, CS (0xF000), IP(0xFFF0), DS wynikają z konstrukcji klocka i tego jak prąd przez niego płynie (jeśli rozpiszecie sobie te liczby na sygnał 1/0 to wszystkie adresy wynikające ze zgrubnego flip/flop staną się oczywiste; pochodzę one z CMOS i są efektem konstrukcji klocka [przy czym można wybrać układem kabli inny “wektor startowy”, na przykład na początku 0x0000, ale z pewnych względów wymaga to porządnego napisania ROMu i raczej niewprowadzania zmian, “bez powodu” dopisały świnie na tablicy], nie widzimisię jakiegokolwiek kodoklepcy). I w nim (0xFFFF0 czyli pierwszym z ostatnich) znajduje się sygnał (bitowy), które bazy rejestrów od prądu odciąć. Oczywiście skrótowo tego tak nie rozpisujemy (tylko na zajęciach albo przy majsterkowaniu) i nazywamy to ładowaniem do rejestru instrukcji. To takie pierwsze poziomy abstrakcji, żeby ludziom się to łatwiej nazywały. Z ludźmi jest jednak taki problem, że jak coś nazwą to później zaczynają myśleć, że to coś istnieje (a nie że jest to pewna ilość cegieł zachowujących wzajemne relacje przestrzenne i zwyczajowo, domyślnie poddana grawitacji). Ponieważ ja tak myśleć nie potrafię to mam pewne ograniczenia (każdy wzorek w głowie mi się rozwija od razu do składowych, co jest dość męczące przy rozwiązywaniu pewnych problemów; ale ma wielkie zalety kiedy potrzebuję coś rozebrać wzrokiem – zazwyczaj linię produkcyjną czy maszynę aby jakąś zagadkę rozwiązać). Oczywiście uczyłem się myśleć w taki sposób, ale nie ta architektura, obsługuję komunikację tego typu wyłącznie pod presją i warunkowo.

Aby władcy maszyny (czyli temu co włączył) uprościć życie w tej “pamięci”, do której mamy teraz dostęp producent płyty głównej podpina inne kabelki. Pierwsze (tradycyjne) 640kilo to “RAM”, później “ROM” i wpinki w płytę (extensions, mycelia) i na końcu flash (z którego zejdą pierwsze instrukcje, co właściwie byśmy od tego prądu chcieli). Wziąłem w cudzysłowie, bo nie jesteśmy na tych kostkach RAM, które wpięliście w płytę, tylko na pamięci flash płyty głównej. Ten duży RAM, który wpięliście nie jest jeszcze uruchomiony, nawet nie dałoby się go adresować po skromnych 20 kabelkach. Oczywiście to jest nieprawda (ale obiecałem nie pisać o mikrokodzie), więc na tym dawniej najniższym poziomie (sprzętowym), a obecnie pierwszym poziomie abstrakcji poprzestaniemy. Nieprawda dlatego, że po drodze są układy w pewnym sensie “programowalne”, ale nie żadnym językiem tylko sitkiem przełączników (coś jak zworek) w chronionej części ROM (tak, możecie sobie przeprogramować cpu, ale raczej tego nie róbcie, “bez powodu” dopisały świnie). Dawniej działało to tak sprzętowo bez uops (mikrokodu), i do każdej operacji był oddzielny układ, ale dziś by się to w obudowie nie zmieściło, więc się tych układów nie multiplikuje tylko oszukuje uopsami właśnie. Przyjmijmy na potrzeby tego tekstu, że opisujemy wszystko jako układy fizyczne, mimo że za naście stron będą opisane przeplotki między nimi, no i jakoś tym trzeba zarządzać (dlatego ROM się grzeje gdy komputer chodzi, mimo że gdzieś napiszę, że się go później nie używa, tej części normalnie dostępnej się nie używa; sitka i owszem).

Istnieje przyczyna, dla której adresy z zerami nazywamy bliskimi, a takie co mają więcej jedynek dalszymi. Każda jedynka to podpięcie tranzystorem kolejnego przepływu dokąś, i żeby zbilansować potencjał na układzie trzeba podnieść napięcie, ociupnkę. Teoretycznie wszystkie wartości powinny być równe, ale ponieważ te klocki są fizyczne, i kabelki do nich (ścieżki) jak najbardziej też, to jedne muszą być bliżej, a drugi dalej od klocka (chyba, że zrobimy magistrale po okręgu, ale na razie nikt się na taki makaron nie uparł) i ustawienia prądu muszą to bilansować. Więc napięcia na pewnych bazach nie tylko otwierają ścieżkę, ale też (pobocznie) sterują dokarmieniem grupy ścieżek większą różnicą potencjału. Obecnie są to jakieś mikroskopijne wartości, ale potrzebne aby układ działał.

Zadaliśmy z tej pierwszej z ostatnich rejestrów flash sygnał (rejestr pamięci) na ustawienie “kabelków” w klocku. Na pamięci flash jest tam zapisane polecenie “przeskocz” (jmp) i jak daleko (far) gdzie będą kolejne rozkazy. Klocek najpierw wykonuje serię operacji podstawowych (wszystkich; mov, add, sub, cmp, jmp) na rejestrach (abcdx, cs, ip i flagach) i jeśli zwrócone wartości są zgodne z tym co w ROM to jedzie dalej, jeśli nie wrzuca OUT na beeper (jęczy; brzęczy jak stara, kiedy się w portfelu nie dodaje). Żeby łatwiej było zapamiętać to te adresy i liczby, do których się woła są zazwyczaj łatwe do zapamiętania (0x1234 – bitwa pod Grunwaldem, po operacji ma wyjść 0x1235 czyli do kiedy trwała) choć czasem zapanuje moda na jakąś liczbę z d. Jeśli nie brzęczy znaczy, że mu ALU i rejestry działają, ponieważ ALU dla flag musi umieć zwrócić and, or i error. Więc plusy i minusy też. Oraz zdolność do ustawiania flag przemocą (mov ax 0, cmp ax 0, jnz [jęcz jeśli nie wyszło]). Przy okazji jest nadużywana instrukcja skoku (jmp), czy aby na pewno wskaźniki instrukcji (IP) są poprawnie przetwarzane (skakanie jest tak pomyślane, żeby sprawdzić każdą kombinację ustawień i nie wyjść z zakresu; dlatego na ekran mogą trafić artefakty bo VGA też czyta swój kawałek z tego obszaru, choć nie powinniście zdążyć zauważyć piksli bez dozoru).

Przekładając na nazwy abstraktów – to niby się powinno nazywać POST i być częścią BIOS, ale dla mnie to są po prostu cegły jakie układamy. Zwiędłym kalafiorem leży mi czy ktoś instrukcje pogrupował i je nazwał. Klocek też ma to nazewnictwo w poważaniu, dają prąd na kable to se prąd płynie, jak wynik jest jakiś to idzie dalej, a jak inny to prąd idzie na brzęczyk. Oczywiście przy odpaleniu brzęczyka podejmowana jest desperacka próba wrzucania czegoś na 0xA000 i dalsze, czyli na ekran (na VGA), no ale jak coś nie działa na tym etapie, to musi się dość finezyjnie zjebać trzeba aby akurat tu zadziałało ponieważ karta graficzna nie była jeszcze inicjalizowana (oczywiście polecenia IN/OUT na chama można próbować wbić; to już zależy od producenta BIOS/UEFI). I oczywiście sprawa kończy się na Niemcu (halt!).

Jak widzicie na razie jedynym poziomem abstrakcji jest zapisanie tego w postaci trzyliterówek, żeby nie pisać zestawów po szesnaście znaków ustawień przełączników w klocki. Od razu podpowiem – tak jest w komputerze cały czas, całe nazewnictwo jest potrzebne wyłącznie dla PICNIC).

Gdybyście oczywiście potrzebowali, to można zmodyfikować BIOS tak, żeby zapisał tę sekwencję do wglądu (na przykład OUT na jakieś kable), ale po co, skoro tu są dwa wyjścia – albo się udało, albo mamy problem. No chyba, że rozwiązujecie takie problemy zawodowo to wtedy wpinacie się przechwyceniem na nóżki (przejściówka na klocka) i grabicie ustawienia pinów na oddzielny sprzęt (bo na przykład prąd może być na nóżce za mały). Czyli na chama wpinacie się w kable. Żeby to robić trzeba mieć powód, w większości wypadków klocek uwalający na tym etapie się wyrzuca i wstawia nowy. Jeśli nowy też nie działa wywala się płytę. W kompie jest też port diagnostyczny (na płycie) i tam przed uruchomieniem brzęczyka jest zrzucany kod błędu. Jeśli podepniecie tam wyświetlacz to wyrzuci jakąś wartość błędu.

Obsługa błędu jest wykonywana dokładnie tak, jak macie to w językach wysokiego poziomu ze składnią try/catch. Po wykonaniu porównania (cmp) albo je st dobrze i idź dalej, albo skocz do obsługi błędu, który poleceniem out zrzuca magic number wpisany do obsługi tego błędu. To jakby ktoś miał wątpliwości dlaczego try/catch wygląda jak wygląda – to jest kopiuj wklej logiki z kloca, tylko ładnie oramkowane, ma dodane dane z przechwyconego błędu, które można wyświetlić, zapisać, dorysować żółtą kaczuszkę i odegrać fanfary.

Pytanie jest czy brzęczyk w ogóle zadziała. Ponieważ jest on programowalnym mycelium (PPI), a jak ustaliliśmy jesteśmy na etapie sprawdzania zupełnie podstawowych funkcji klocka. Bo po pierwsze trzeba mu załadować z rejestru timer (jak długo ma wyć) i wysłać na urządzenie, trzeba do rejestru załadować częstotliwość… i wysłać na urządzenie (brutalnym out), załadować do rejestru ustawienie kabelków włączające głośnik, później zamulacz cykli w pętli ileś razy i wyłącznie. Jeśli kiedyś przysmażyliście klocka czy płytę na tyle, że nie ogarniał kuwety na tym etapie, a na diagnostyce wywalał kod błędu (bo mieliście podpięty wyświetlacz), to zdarzało się, że nie był nawet w stanie zawyć. Bo klocek dużo prądu zniesie, ale wysoka częstotliwość na flashu (zmiana taktowania fizyczna, zworkami kiedyś) mogła przegrzać kostkę do pamiętania i przebijała bzdury z rejestrów obok. Szczególnie latem.

Na chwilę skupmy się na poleceniu ADD. Ono obsługuje wyłącznie int16. Dawniej do add int i add float były da oddzielne układy, obecnie jest to jeden, “duży” układ gdzie int adder z carry to jest po prostu podpięcie się na krótko do części układu, zresztą mul in i mul float są też częścią tego układu, a addedry są w nich hurtem i na pęczki, w trybie rzeczywistym mamy podpięcie na krótko tylko do pierwszego addera int i tylko dla 16 młodszych bitów. Ma on tę zaletę, że zwraca wynik od razu, w jednym cyklu. To, że te układy dało się tak zintegrować wynika z liczby warstw jakie można ułożyć w klocu, bo te “u góry” chodzą na wyższym prądzie, a logiczne, że jak przepuszczamy sygnał dłuższą ścieżką (kodowanie float, zrównaj przecinkami, pomnóż, dodaj wyniki, rozkoduj na float znak, cecha, mantysa) to trzeba więcej prądu zapiąć na układ. Tak sam z siebie to grzał się nie będzie. Ale gdybyście obejrzeli klocki sprzed 30 lat, to ALU dla i/f były rozdzielone i do tego oddzielnie były addery i oddzielnie mul z adderami. Ścieżki były grube, więc za daleko z tym prądem nie można było zejść po kabelkach, tylko szybko wracać na rejestr zanim zgaśnie. Przy okazji testów addera nowsze klocki sprawdzają też AL i AH, czyli 8bit sumy, bo 16bit można puszczać w częściach. ALU 32 i 64 bitowe przetestuje dopiero dalsza część kodu (po odpaleniu urządzeń rozszerzających procek o funkcje, czyli obudzeniu reszty klocka w innym trybie – protected i long).

Wrzucane jest kilo wpisów, znaczy ćwierć kila wpisów po cztery bity na początek RAMu (jeszcze nie wiadomo czy działa^^). Od teraz ta część pamięci jest dla tablicy (właśnie wrzuconej) przerwań. I każdy zawiera adres gzie jest obsługiwany. To są przerwania systemowe czyli mycelia, które podłączacie do kompa, żeby go wykorzystywać bardziej, klawiatura, wyświetlacz, hdd. Zapisywane jest to w RAM, ponieważ późniejsze operacje (zbiorczo określane jako OS) dostaną prawo tam grzebać. Najstarsze zapisy dotyczą pierwszych urządzeń i objawów głupoty (dzielenie przez zero, zegarynka), o które w kolejnych krokach będzie warto pytać do wykonania operacji.

Kiedy już przez to przeszedł klocek sprawdza, czy mu RAM (te pierwsze 640kilo) gra i koliduje, oczywiście zapisane wcześniej IVT (dysk, klawiatura, dzielenie przez zero oraz rezerwę na później) pomija w zapisywani – nie ma tam co grzebać, ale sprawdza czy czyta. Jak tego nie czyta wyjdzie z brzęczykiem, że rama pękła i spadł z rowerka. Czyli będzie w tym pisał i czytał (marsz, w/r), żeby się dowiedzieć czy nie ma przebicia między komórkami (przeplot), czy szyna danych cała, czy adresowa działa (kolizje), czy komórki pamięci uszkodzone (stuck).

Logiczny odpowiednik IVT w telefonie zawiera takie graty jak żyra, akcelerometry i podobny szpej, którego nie odłączacie i nie podpinacie, sobie po prostu rezyduje na płycie. Po prostu w telefonie łatwiej jest przewidzieć co jest na pokładzie, a przynajmniej co tam być powinno, głownie po to, aby kodoklepca wysokiego poziomu nie zaprzątał smyrfona głupimi pytaniami pod jakim adresem jest przerwania dla żyra, tylko zgodnie ze standardem jest tam gdzie powinno read only.

I dopiero tutaj klocek zajmuje się pierwszym poziomem abstrakcji. Na potrzeby użyszkodnika oczywiście, ponieważ użyszkodnik potrzebuje ochrony przed własną niefrasobliwością i dla porządku w klocku zadał takie dyspozycje. Oczywiście użyszkodnik jeśli chce to może te dyspozycje zmienić. Ponieważ BIOSy wymarły na rzecz UEFI (akurat ja to piszę na klocku jeszcze z BIOSem, węgiel trzeba sypać równo, trzy zasilacze już przechodziło gówno, a wentylator klocka na słowo trytytki trzyma się gdzie powinien) to można w EFI sobie napisać i podpisać zmiany jako machine owner (do zmiany w UEFI trzeba się podpisać). Zasadniczo – nie róbcie tego w domu, chyba, że akurat odczuwacie taką potrzebę. Ale pierwsze testy polecam robić na kompie, którego Wam nie żal (po babci), i jak już zrobicie pod takie mody własny kernel to raczej nie podpinać go do netu tak, chyba, że bardzo chcecie się dowiedzieć co jeszcze by wypadało załatać w systemie. Zmiany tego typo są powszechne, jeśli zastanawiacie się, dlaczego maszyn CNC raczej nie wpina się do sieci to jest to jeden z listy powodów działa – nie psuj. Jeśli bladawiec ze stoczni drze japę na firmę od spawarek, że spiął je w sieci (żeby zbierać dane czy się który nie leni) i przestały działać, to grzecznie pytają kiedy ostatni raz serwis robił mu update firmwaru, bo tam karty stare i dziurawe. Zazwyczaj okazuje się, że były cięcia i…

Ten pierwszy poziom abstrakcji to zmapowanie przerwań urządzeń PIC na jakieś zapasowe adresy z IRQ. Robi to brutalnymi komendami out konfigurując urządzenia do działania w trybie (czyli wysyłając im z rejestru sygnał w jakim trybie będą od teraz kołchozić). Ustawia tak zegarynkę systemową, klawiaturę, i w pamięci przydziela gdzie one zwracają swój kod. Po to, żeby kolejne procesy nie musiały korzystać z in/out tylko, żeby co jakiś jiff z PIT miały update w pakiecie (to jeszcze nie jest jiff, który ustawimy kilka pięter wyżej w abstrakcjach jako pętle programowania, ale to wyżej wzięło się z tych interwałów niżej).

Dalej ustawiane są kwestie socjalne – kto z kim może gadać i w jakim trybie (kto jest panem, kto sługą na której ścieżce) tak, żeby klockowi dupy nie zawracać pierdołami. Są to ustawienia bezpośredniego dostępu do pamięci przez oddzielne, małe klocki, tak żeby RAM z HDD sobie pogwarzył. Przydziela im kanały, ścieżki, porty, bloki w jakich wolno im wołać i odpowiadać cały ten szpej ładując do ich rejestrów (na chama, ciągle na out, żadnych sterowników jeszcze nie ma w użyciu). Oczywiście zawsze może im równie brutalnie poprzestawiać, ale dla konwencji się tego raczej nie robi. Chyba, że chcecie używać jakiejś fikuśnej konfiguracji szybkich dysków do jakiś wyjątkowo fikuśnych miningów.

Przychodzi czas na ustawienie PITa (to te jiffy wyżej), to taki systemowy budzik dla dłuższych pętli, dla uruchamianych programów. Inicjalizację grafiki (z tej części 0xA000 i dalej) na (miejmy nadzieję podpięty) wyświetlacz. Zwyczajowo włącza karcie graficznej tryb tekstowy, i maca jej ROM żeby wykonać. Robi out na klawiaturę i sprawdza czy wróciło. Uruchamia źródła danych (flopa, ide) i maca czy odpowiadają (flopy to zdaje się do dawna nie odpowiadają, ale sprawdzić nie zaszkodzi, a nuż!). Jak odpowiadają to resetuje kontroler i pyta, który jest pierwszy (dysk w środku to nie jest jeden dysk, tylko cała paczka nawet jak jest tylko jeden, no i na tym pierwszym jest ewentualny sektor rozruchowy o ile coś tu ma być rozruchane). A później kontynuuje spacerek po IVT sprawdzając kto jeszcze odpowie – może scsi, może karta sieciowa istnieje, warto się rozejrzeć. Jak któreś urządzenie jest, ale coś słabo (bo się trytytka poluzowała) to wrzuca na brzęczyk, żeby zajrzeć do pudła (albo w nie mocniej huknąć, też pomaga – co wypadnie trzeba włożyć z powrotem, choć może jak wypadło i działa to niepotrzebne było?).

Zwracam uwagę, że klocek to wszystko robi na 1mega ram/rom w trybie 16bit. Oczywiście na UEFI szybko przejdzie w tryb 32bit, ale… Ala jest ważna. To jest implementowane wyłącznie dlatego, że kompilatory z wysokich poziomów mają cały zestaw narzędzi do symulacji 32 i 64 bardzo dobrze przetestowany (duża próba), a na małe klocki cała historia badań ma zebrane tyle danych, co z nowoczesnych klocków schodzi w ciągu miesiąca z fabryk. Po prostu wcze nigdy nie robiono tylu klocków i tylu testów “co tu się może spier…”. Jednakże większość inicjalizowanych urządzeń jest sterowana (In/Out) na 4 i 8bit, niektóre na 6 (przycięte 8), garstka na 16. Więc te 32 bitowe rejestry i tak chodzą puste. Kolejność testowania podzespołów się nie zmienia. Podobny system chodzi na GPU (to też jest obecnie klocek, który na rozkaz z proca ma się podnieść i odmeldować, że słyszy i pozostaje posłuszny), one są zbieżne dla wszystkich urządzeń. Nawet jak sobie weźmiecie pająka imitującego 6bitowy komputer to musi sprawdzić dokładnie to samo, nawet jeśli obsługuje pamięci tyle co za paznokciem. Musi sprawdzić czy rejestry działają, czy liczydło niezepsute, czy jest kontakt z rzeczywistością (I/O). Jeśli przejrzycie wykonywane instrukcje to to jest w kółko to samo, większość na inkrementalnych pętlach. Jedyną różnicą w kodzie (wpisaną ręcznie) jest obsługa błędów i ręcznie wklepane ustawienia numeru błędu oraz próby załączenia brzęczyka, diody, grafiki. Sprawdzanie integralności ALU (moduł po module w hierarchii zagnieżdżeń od środka) od i2/f2 (faza dla liczb urojonych) czy innego flagowania po 64bity różni się tylko ile trzeba wpisać w rejestr. I spokojnie można tryb kalkulacji 64 bit odpalić na 4 bitowej szybie, po prostu załadunek i rozładunek zajmie wiele cykli, ale programy szyfrujące gdzie tysiące bitów długości ma liczba to niby co innego robią w pętlach? Przecież to się na urządzeniu wykonuje, w środku nie ma nic więcej. To że akurat tryb rzeczywisty zaczyna się od 16bit (choćby garść cykli do startu uefi) to tylko taka przypadłość historyczna. Kompatybilność wsteczna, działa – nie psuj.

Przejdźmy dalej, bo nam 16 bitowy klocek na 1mb flasha z płyty nie bardzo potrzebny. Trzeba wstać całą resztę ustrojstwa. Tu się rozbiega technologia, ale zaraz się zbiegnie. Albo bootloader to zrobi albo uefi. Ważne po co odbędą się kolejne kroki.