Architektura komputerów 2 Prof. PWr dr hab. inż. Janusz Biernat Wykład 01 MODELE PROGRAMOWE I JZYKI ASEMBLEROWE Najbardziej podstawowym zapisem algorytmu jest ciąg zerojedynkowy, bo jest akceptowany bezpośrednio przez procesor. Jego wygoda jednak jest średnia. Pierwszy kompilator powstał w roku 1956. Kompilator to program tłumaczący treść algorytmu w pewnej formie opisu środowiska (czyli w formie tekstowej - międzynarodowym kodzie ASCII, dzisiaj też UTF-8, np. Java) na treść wynikową, która sama z siebie nie jest jeszcze programem. Jako że niewygodnym byłoby to, żeby kompilator tworzył gotowy program - ze względu na to, że niemożliwe byłoby wtedy łączenie niezależnych modułów programów Kompilator: treść programu (plik zródłowy) + opis środowiska w formie tekstowej || (międzynarodowa część kodu ASCII: treść wynikowa 00 16-7F 16 (plik wynikowy) czyli cyfry dziesiętne (to nie jest jeszcze program) i znaki alfabetu łacińskiego) Kompilator zajmuje się tylko sprawdzeniem formalnej poprawności zapisu algorytmu w danym języku. Nie sprawdza w ogóle semantyki i jego ogólnej sensowności. Konsolidator: łączenie modułów (treści wynikowych) w tzw. plik wykonywalny nagłówek z (linker) informacjami dodatkowymi (co można, czego nie można robić z kodem) + kod. Program ładujący: 1) skopiowanie kodu pliku wykonywalnego do pamięci głównej (z (w tej chwili której korzysta procesor). podanie nazwy 2) przekazanie sterowania (skok) do pierwszej instrukcji. pliku wywołuje automatycznie program ładujący) Plik zródłowy: (nieprzenoszalny) (przenoszalny) " zapis algorytmu model programowy procesora / maszyny wirtualnej, " dyrektywy dla kompilatora konstrukcja kompilatora i środowiska. W językach asemblerowych dużych programów już się nie pisze. Co najwyżej jakieś małe ich elementy, albo sterowniki. Model programowy procesora wyszczególnienie dostępnych zasobów procesora (ograniczenie dotyczy dostępu do rejestrów specyficznych). Dostępne są dwa poziomy : użytkownika i nadzoru. Wszystkie zasoby procesora dostępne są tylko w tym drugim - na przykład reguły wzajemnej komunikacji procesów, ustawianie rejestrów specjalnych, zmiana zasad ochrony pamięci. Model ten składa się z następujących trzech podstawowych elementów : " rejestrów, " listy rozkazów, " słów w pamięci operacyjnej. Wskazywanie dostępu do listy rozkazów i rejestrów jest podobne w architekturach Intel 8086, Intel IA32 i Intel Pentium. Występują jednak dwa modele pamięci, czyli pamięć liniowa i pamięć segmentowana. Model programowy: nr. segmentu=0 lista rozkazów pamięć główna adres=0 rejestry (procesor) (słowa w pamięci) adres w segmencie Modele pamięci: Pamięć liniowa (używana na laboratorium) łatwiejsza, wirtualny model pamięci (wszystkie segmenty w jednym miejscu). Pamięć segmentowa trudniejsza, Intel odwołuje się do tego modelu: wskaznik / adres nr. segmentu : adres w segmencie intel 8086 IA32 PENTIUM Wyrażenie: (dyrektywa kompilatora) opis działania i argumentów według ustalonej składni: mnemonik argument , argument (skrót nazwy działania) swobodny akumulacyjny add (nowa wykładnikowa (stała, zródłowa adc zawartość) zawartość) sub arg_akumul := arg_akumul działanie arg_swobodny ... (argument zapisany w mnemoniku) rozgałęzienie_warunek adres docelowy (mnożnik) mul argument (mnożnik zawsze zawartość rejestru a) Specyfikacja argumentów: najwyżej jeden argument może być słowem w pamięci. Rodzaje: stałe: - deklaracja (stałe symboliczne): NAZWA = (typ) lista wartości (jest to deklaracja statyczna i obowiązują w całym programie) - użycie jako argumentu: $NAZWA $0xFC stała szesnastkowa $0q75 stała ósemkowa $0b01 stała dwójkowa $0d59 stała dziesiętna 59 stała dziesiętna przy stałych liczbowych musi być podany system (chyba, że stała jest dziesiętna) (deklaracja dynamiczna EQU) rejestry: 31 15 8 7 1 a |________|____|____| ecl nie ma b *h *l ecx !! c _________ w niektórych cx d *x instrukcjach zawiera cl (*) __________________ domniemany ch e*x argument (czasami si si 31 15 1 też w d, a w jednym esi bp |_________|________| przypadku w c) sp ** (**) __________________ e** użycie rejestrów: %nazwa_rejestru %eax, jeżeli napiszemy bez % kompilator potraktuje to jako zmienną albo etykietę, a wtedy może być ciężko znalezć błąd. wskazanie słów w pamięci (zmiennych): zmienne: - deklaracja nazwa_zmiennej typ lista_wartości (może być też bufor czyli zmienne bez wartości początkowej) .byte, .ascii, .word, .short, ... -atrybuty - adres (w prawdziwej architekturze Intela jest on podwójny, bo segment i przesunięcie w segmencie) - wartość - typ - rozmiar (zawsze w bajtach) - użycie zmiennych nazwa_zmiennej (wartość słowa wskazanego przez tą zmienną) $nazwa_zmiennej (adres zmiennej) zmienne indeksowane: nazwa_zmiennej(%rejestr) - indeksowanie za pomocą jednego wskaznika bez skalowania nazwa_zmiennej(%rejestr_b, %rejestr_indeksowany, skala) (bazowy, (skalowany) (1,2,4,8) nieskalowany) adres_faktyczny=%nazwa_zmiennej+(rejestr_b)+[rejestr_indeksowany*skala] nazwa_zmiennej(, %rejestr-indeksowany, skala) W tym ostatnim trybie współczynnik skali określa, o ile bajtów się przesunąć przy jednym cyklu , tj. ile bajtów zajmuje nasze słowo w pamięci. .align 32 dyrektywa porządkująca (tak ustaw, żeby adres był podzielny przez 32) JOLA .ascii Architektura komputerów\n eax=5, ecx=2 4( ) - $ nie musi być JOLA(%eax,%ecx,1) 5+2*1 $0xF( ) - $ musi być koniecznie! rozgałęzienia: j warunek adres_docelowy (jedna z etykiet w treści programu) "stan wskaznika z rejestru flag CF C-carry, Z-zero, S-sign, OV-overflow, NC-notcarry, NZ-notzero, NS-notsign "wynik porównania - najbardziej sensownym warunkiem wydaje się być porównanie dwóch liczb. Możemy tego dokonać wykonując instrukcję cmp X,Y (przy czym pózniejsze warunki to zawsze Y (warunek) X), a następnie skok w zależności od wyniku tego porównania : - liczb naturalnych A-above, B-below, E-eqal - liczb rzeczywistych GT-graterthen, LT-lessthen, E-eqal, GE-greateroreqal, LE-lessoreqal loop etykieta 1) zmniejsz ecx 2) sprawdz czy ecx=0 3) TAK skok do etykiety, NIE kontynuuj