Assembly Iványi Péter
Miért? Ma már ritkán készül program csak assembly-ben Általában bizonyos kritikus rutinoknál használják Miért nem használjuk? Magas szintű nyelven könnyebb programozni Nehéz más gépre átvinni a programot Miért tanítjuk? Az assembly rutin kisebb és gyorsabb lehet Közvetlen hardware elérés, amit magasabb szintű nyelvből esetleg lehetetlen Mélyebb tudás a számítógépekről Fordítók megértése, magasabb nyelvek megértése
Miért? Sajnos kis és gyors rutinok írásához nagy gyakorlat, tapasztalat kell A rendszer részletes ismerete Assembly esetén lemondunk szinte minden rendszer támogatásról és a saját kezünkbe vesszük a vezérlést Gondosan válasszuk meg a programozási eszközt Általában a középút az ideális Program készítés Karbantartás Költség, munkaráfordítás, stb.
Miért? Hatékony, gyors, kis program létrehozása idő és munkaigényes Milyen célt szolgál a rutin? Hányszor fut le? Ha csak egyszer fut le, minek javítani? Ha többször, pl. JIT compiler nem elég?
Mit? IBM PC Intel processzorok Windows (Linux) Programozás alacsony szintű nyelven Magas szintű nyelvek által generált kód
Számítógép története Ez most kihagyjuk! Ismétlés Számrendszerek közti konverzió 2, 0, 6 alapú számrendszer Számítógép architektúrája Végrehajtás Regiszterek Memória címzés Megszakítás
Tizes számrendszer Számjegyek: 0-9 Példa: 0 0 2 2 0 0 0 0 0 + + + + + d d d d d n n n n K 3045 5 40 0 3000 0 5 0 4 0 0 0 3 3045 0 2 3 = + + + = + + + =
Kettes számrendszer Számjegyek: 0- Példa: 0 0 2 2 2 2 2 2 2 + + + + + d d d d d n n n n K 45 0 4 8 0 32 2 2 0 2 2 2 0 2 00 0 2 3 4 5 = + + + + + = + + + + + =
Tizenhatos számrendszer Hexadecimális számok Számjegyek: 0-9,A,B,C,D,E,F Példa: 0 0 2 2 6 6 6 6 6 + + + + + d d d d d n n n n K 943 5 6 0 256 3 6 5 6 0 6 3 3AF 0 2 = + + = + + =
Konverzió Mennyi decimális 45 binárisan? 45 / 32 (2 5 ) = maradt 3 3 / 2 4 = 0 maradt 3 3 / 2 3 = maradt 5 5/ 2 2 = maradt / 2 = 0 maradt / 2 0 = maradék nulla 45 0 = 00 2
Összeadás Mindegyik számrendszerben működik
Előjeles számok Hogyan reprezentáljuk a negatív számokat? Egyszerű megoldás: Használjunk egy bitet az előjel jelölésére S= 0 pozitív (+) S = negatív (-) Probléma: Két darab zérus értékünk van 000 0000 0000 0000
Kettes komplemens Előjeles számok d n n 2 + d n 2 0 n 2 + K+ d2 2 + d 2 + d0 2 Negatív szám (n) előállítása: Invertáljuk a biteket és adjunk hozzá -et Vegyük a legnagyobb számot (csupa ), vonjunk ki n- et, majd adjunk hozzá -et Jobbrol balra haladva másoljuk a zérusokat, az első - est is másoljuk, a többit invertáljuk
Kettes komplemens 00 000 00 0 00 7 6 5 4 3 2 000 000 000-8 0 0000 00 00-7 -6-5 -4-3 -2-0 0 00 0
Kettes komplemens. 8-bites számok, előjel nélkül: 0-255 45 0 = 000 0 2 Invertáljuk 0 000 2 Adjunk hozzá -et 0 00 2 Eredmény: Ellenőrzés 000 0 + 0 00 0000 0000 0 00 2 =2 0 =-45 0
Kettes komplemens 2. 45 0 = 000 0 2 A legnagyobb szám 2 =255 0 Vonjunk ki 45-öt 255-45 = 20 0 = 0 000 2 Adjunk hozzá -et 0 00 2 Eredmény: 0 00 2 =2 0 =-45 0
Kettes komplemens 3. 28 0 = 000 00 2 Másoljuk a zérusokat jobbról balra 00 2 Másoljuk le az első -est 00 2 Invertáljuk a többi bitet 0 000 2 = 228 0 Ellenőrzés 000 00 + 0 000 0000 0000
8 bit esetén Kettes komplemens - = -2 = 0-28 = 000 0000 + = 0000 000 +27 = 0 +28 = érvénytelen!!!
6 bit esetén Kettes komplemens - = -2 = 0-32768 = 000 00000000 0000
8-ból 6 bites számok 64 0 8 bites reprezentálás: 40h 6 bites reprezentálás: 0040h -64 0 8 bites reprezentálás: C0h = 00 0000 6 bites reprezentálás: FFC0h = 00 0000 Szabály: Az előjel bitet minden további bit helyébe bemásoljuk
-448 6-ból 8 bites számok 6 bites reprezentálás: FE40h 8 bites reprezentálás:nem lehet!!! Sign contraction csak akkor lehetséges ha az összes felső bit mind zérus vagy -es Példa: FF80h -ból 80h lesz
8086-os processzor Regiszterek Memória Architektúra
6 bites processzor Busz Címbusz 8086 A processzor a memóriával közli a kívánt címet Adatbusz A processzor és egyéb egységek közötti adatfrogalomra Vezérlőbusz A vezérlőjeleket továbbítjuk Az adatbusz vonalainak száma adja meg hány bites a processzor
8086 címbusz 8086 rendszer adatbusz 6 bit Memória és egyéb egységek vezérlőbusz
Intel processzorok Processzor Adatbusz Címbusz Memória 8088 8 20 MB 8086 6 20 MB 80286 6 24 6MB 80386dx 32 32 4GB 80486 32 32 4GB 80586/Pentium 64 32 4GB
Egyszerű mikroprocesszor Kezdés Következő utasítás betöltése Utasítás dekódolása Utasítás végrehajtása Vége
Egyszerű mikroprocesszor, 8086 Fetch Tölt Dekód Futtat Fetch 2 Dekód 2 Futtat 2 Processzor Vár Vár Tölt Vár Vár Busz idő
80486 mikroprocesszor Busz Fetch Fetch 2 Fetch 3 Fetch 4 Tárol Fetch 5 Fetch 6 Dekód Dekód 2 Dekód 3 Dekód 4 Vár Dekód 5 Dekódoló egység Futtat Futtat 2 Futtat 3 Futtat 4 Vár Végrahajtó egység Címzés Vár Vár Címzés 2 Címző egység pipelining
Vezérlő regiszterek IP (Instruction pointer) Utasításmutató, az éppen végrehajtandó utasítás címe SP (Stack pointer) Veremmutató, utoljára beírt elem címe BP (Base pointer) Bázismutató, a verem címzéséhez SI (Source index) Kiindulási terület indexe DI (Destination index) Célterület indexe
Általános regiszterek AX (Accumulator register) Általános célú, majdnem mindenre használható matematikai műveleteknél gyakran használjuk AL (Low) és AH (High) részre osztható AH AL 8 bit 8 bit 6 bit
BX (Base register) Általános regiszterek Általános célú Főleg címzésnél használjuk BH BL 8 bit 8 bit 6 bit
Általános regiszterek CX (Counter register) Számláló regiszter, ciklusszervezésnél használjuk CH CL 8 bit 8 bit 6 bit
DX (Data register) Általános regiszterek Adat regiszter Szorzás és osztás esetén speciális szerepe van DH DL 8 bit 8 bit 6 bit
CS (Code segment) Szegmens regiszterek Az aktuális program báziscíme Minden utasításelérésnél használja a CPU Módosítása csak vezérlésátadással SS (Stack segment) A veremként használt terület szegmensének címe Csak ha nagyon szükséges, akkor módosítsuk
DS (Data segment) Szegmens regiszterek Adatszegmens regiszter A címzett területről lehet adatot elérni Írható/olvasható ES (Extra segment) Extra szegmens regiszter Másodlagos memória terület elérésére szolgál
Státusz regiszter 0. Bit (Carry): átviteli bit, egy 8 bites művelet 9. bitje. Bit (Parity): értéke egy, ha az eredmény byte-ban az egyes bitek száma páros, különben zérus 4. Bit (Auxiliarry carry): BCD számok kezelése esetén használt 6. Bit (Zero): ha az utolsó művelet eredménye zérus, akkor az értéke, egyébként zérus
Státusz regiszter 7. Bit (Sign): értéke zérus ha az eredmény pozitív, egyébként 8. Bit (Trap): debug-golásnál használt 9. Bit (Interrupt): megszakítás tíltás vagy engedélyezés, ha minden megszakítást fogad 0. Bit (Direction): string kezelő műveletek használják, ha értéke zérus, akkor az SI és DI regisztereket automatikusan növeli, ha egy akkor csökkenti
Státusz regiszter. Bit (Overflow): túlcsordulás jelzése
32 bites regiszterek
Veremkezelés Alapvető fontosságú!!! Függvények közötti kapcsolat Visszatérési cím Paraméterátadás Visszatérési érték
Függvényhívás void f3(int a3) printf( %d,a3); void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a b akármi visszatérési cím (argumentum) void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 visszatérési cím (argumentum) void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 visszatérési cím void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 visszatérési cím void f() int a = ; int b; f2(a); Ekkor is függvényhívás történik, de most ignoráljuk.
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 visszatérési cím void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 visszatérési cím void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 2 visszatérési cím 2 (argumentum) void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 2 visszatérési cím 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 2 visszatérési cím 2 void f() int a = ; int b; f2(a); Ekkor is függvényhívás történik, de most ignoráljuk.
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 2 visszatérési cím 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 a3 2 visszatérési cím 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP a2 2 void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi visszatérési cím void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP void f() int a = ; int b; f2(a);
Függvényhívás void f3(int a3) printf( %d,a3); a b akármi void f2(int a2) f3(a2); a2 = (a2+); f3(a2); SP void f() int a = ; int b; f2(a);
Függvényhívás A függvényhívás során a verem adja a függvény környezetét, állapotát Argumentumait Hova kell a függvény után visszatérni
Számok: Adattípusok bit: 0 vagy nibble: 4 bit DB: byte = octet = 8 bit DW: word (szó) = 2 byte = 6 bit DD: double word = 4 byte = 32 bit Szöveg: Karakterek, ASCII kód, 8 bit
8086-os címzés Real mode Memória címzés 20 bites lineáris memória címet kell generálni 6 bites címekből Szegmens címet 4 bittel balra toljuk, majd egy offset címmel összeadjuk + = szegmens 0000 offszet lineáris cím 6 bit 6 bit 20 bit
Lineáris cím: 25553 Példa Szegmens:offszet = 2222:3333 Szegmens:offszet = 2000:5553
Szegmentálás, Intel 8086 (régi szegmentálás) Max MB memória 64 KB szegmensek 6 x 64 KB = MB Bármely cím elérhető, csak be kell tölteni a szegmens regiszterbe CS : kód szegmens szelektor DS : adat szegmens szelektor
Szegmentálás, Intel 80386, védett módú címzés 4 GB memória A memória nem elérhető szabadon A szegmens bármekkora lehet byte 64 KB 4 GB
Szegmenstáblák GDT (Globális leíró tábla) Csak egy van Általános célú Minden program használhatja IDT (Megszakítás leíró tábla) Megszakítás kezelő rutinok címe LDT (Lokális leíró tábla) Minden processzusnak lehet Opcionális
Szegmenstáblák Miután a memóriába helyeztük a leíró táblákat, a processzornak is meg kell mondani 3 speciális regiszter: GDTR, IDTR, LDTR 32 bites regiszterek Szegmens regiszter (DS, CS) 6 bitesek
Szegmens címzés Hogyan lehet egy 6 bites regiszterben 32 bites címet tárolni? Sehogy Vesszük a 6 bites címet Eltoljuk jobbra 3 bittel (3 bites cím) Ez 892 különböző értéket jelent, ami pont megfelel a szegmenstáblák méretének A szegmens regiszter a szegmenstáblán belüli indexet adja meg
Mire kell az alsó 3 bit? Szegmens címzés 2 bit: a védelmi szint: 0-3 bit: GDT vagy LDT táblát használjuk-e?
Szegmens címzés FFFF FFFF DS:ESI DS:0 szegmens ESI 32 0 DS 5 0 00000 0000 00 0 XX GDT GDTR 48 0 0000 0000
Szegmens címzés, LDT-vel FFFF FFFF DS:ESI ESI 32 0 DS:0 szegmens LDT DS 5 0 00000 0000 000 XX LDTR GDT GDTR 48 0 0000 0000
Program és adatterület szervezése Eredeti SP SS:BP SS:SP Felhasznált verem Szabad verem SS CS:IP DS:DI DS:SI Program Adat CS DS
Interrupt Megszakítás A normál végrehajtási folyamat megszakítása Egy folyamat felfüggesztése, a folyamathoz képesti külső esemény hatására, olyan módon hogy a folyamathoz vissza lehet térni Hardware események váltják ki
Megszakítás kezelő Felhasználói mód alkalmazás Kernel mód megszakítás kezelő
Megszakítás A 8086-os processzor egy 256 elemű megszakítás rendszerrel rendelkezik A memória elején, a 0. címen található a táblázat Minden interrupthoz egy 4 byte hosszú blokk tartozik. szó: IP 2. szó: CS
Megszakítás lépései A külső egység a processzor egyik lábán jelzi, hogy megszakítást kér A proc befejezi az utasítás végrehajtását A kérő egység elküldi 8 biten a megszakítás számát Valójában egy interrupt kezelő van a proc és az egységek között, Ez sorolja be a megszakításokat, ha több is jön egyszerre
Megszakítás lépései A megszakítás számának beolvasása után Elmenti a státusz regisztert Elmenti IP és CS regisztereket a veremre A sorszámnak megfelelő CS és IP értéket kiolvassa a táblázatból és betölti Letiltja a megszakításokat Az Interrupt bitet törli Végrehajtja a megszakítás rutinját
Megszakítás lépései A megszakítás rutin egy IRET paranccsal tér vissza Visszatölti a státusz, CS és IP regisztereket
Szoftver megszakítás Programból is kiváltható a megszakítás az INT utasítással!!! Software interrupt A szubrutin hívás egy speciális formája A memória bármely részére átadhatjuk a vezérlést Nem címet adunk meg, hanem egy megszakítás számot Programok közötti kommunikációra is alkalmas 60h -7Fh : user interruptok