Assembly Rekurzív függvények, EXE, C programok Iványi Péter
Algoritmusok előadás Rekurzív függvény
FÜGGVÉNY nyomtat(n) print n HA n!= 0 nyomtat(n-1) ELÁGAZÁS VÉGE FÜGGVÉNY VÉGE Rekurzív függvény
org 100h mov ah, 02 push 04 call nyomtat add sp,2 int 20h Rekurzív függvény nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
Rekurzív függvény org 100h mov ah, 02 push 04 call nyomtat add sp,2 int 20h SP 4 visszatérési cím BP BX 0
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 0 4 visszatérési cím
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 0 4 visszatérési cím BP tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 0 4 visszatérési cím BP tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 0 4 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 4 4 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 4 4 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 2 És így tovább 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma BX tartalma DX tartalma
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 3 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 4 4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
nyomtat: push bp mov bp,sp push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret Rekurzív függvény SP BP BX 4 4 visszatérési cím
org 100h mov ah, 02 push 04 call nyomtat add sp,2 int 20h Rekurzív függvény 4 SP BP BX 4
org 100h Rekurzív függvény mov ah, 02 push 04 call nyomtat add sp,2 int 20h SP BP BX 4
EXE és COM programok, kitérő Eddig: COM Csak egy szegmens EXE Több szegmens lehet Különbség: Objektum formátum
COM Azonnal futtatható COM programok A rendszer betölti a memóriába A DOS a 100h címre adja át a vezérlést Kód szegmens relokálható A program mindig ugyanarra az offset-re töltődik be De a kód szegmens szabadon változhat Minden cím állandó a programban
EXE EXE programok Nem lehet azonnal futtatni Bizonyos címeket nem lehet fordítási időben meghatározni, futáskor kell kiszámolni A file-nak van egy fejléce, amit a linker hozott létre Relokációs tábla Megadja CS, IP, SS, SP regiszterek értékét A program bármilyen címen kezdődhet Kilépés MOV AH, 04Ch INT 21h
EXE program fejléc ex_magic dw? ;.exe signature [MZ vagy ZM] ex_lpage dw? ;.exe file hossza mod 512 bytes < 512 bytes ex_fpages dw? ;.exe file hossza div 512 bytes ha ; Lpage>0,akkor hozzáadunk még egyet ex_relocitems dw? ; relokációs tábla hossza ex_hdrpara dw? ;.exe fejléc mérete paragrafusokban ex_minalloc dw? ; minimális memória ex_maxalloc dw? ; maximális memória ex_oss dw? ; SS [verem szegmens] ex_osp dw? ; SP [verem offset] ex_chk_sum dw? ; checksum ex_oip dw? ; IP [innen kezdődik a program] ex_ocs dw? ; CS [ez a szegmense] ex_ofstbl dw? ; az első relokációs tábla offset-e ex_ovrnum dw? ; overlay-ek száma
EXE program segment data msg db "hello, world!", 0dh,0ah, '$' segment stack stack resb 30 segment code..start: mov ax, data ; szegmens regiszter beállítása mov ds, ax mov es, ax mov dx, msg ; nyomtatandó szöveg mov ah, 09h int 21h mov ah, 0 ; billentyű lenyomásra várunk int 16h mov ah, 4ch ; kilépés int 21h
EXE program A program hol kezdődik?..start: Különböző szegmenseket lehet definiálni Kód szegmens Adat szegmens Verem szegmens Például: segment data
EXE program fordítása hello.asm program forráskód assembly hello.obj program tárgy modul linking hello.exe futtatható program object module library object module library egyéb tárgy modul
EXE program fordítása Tárgykód létrehozása nasm -fobj hello.asm Futtatható file létrehozása alink hello.obj -o hello.exe Külső referenciák feloldása, linkelés Eredmény: hello.exe 150 byte
EXE relokáció A rendszer csak a betöltés során határozza meg hol fut a program Több szegmens van, így ez probléma lehet COM esetén nincs ez a probléma LINK úgy készíti el a programot mintha a 00000h címen kezdődne Betöltéskor ismerjük a CS, DS címét A kódban elszórt címekhez kell a CS és DS aktuális értékét hozzáadni Erre szolgál a relokációs tábla
Relokációs tábla: EXE relokáció Minden bejegyzés két szavas (szegmens:offset) A program terület kezdetétől számítva adja meg a módosítandó címet A rendszer minden betöltéskor felülírja a programterületet Nem túl elegáns, de jól működik Biztosítja az áthelyezhetőséget
hello.exe relokációs tábla mérete relokációs tábla címe 00 01 02 B8 00 00 módosítandó cím: 0003:0001
hello.exe Program kezdete: 0003:0000
Kapcsolat C programmal C programok ugyanazt a függvényhívási konvenciót használják Minden függvény egyenlő! Paraméterátadás a vermen keresztül Visszaadott érték Vermen keresztül AX regiszteren keresztül
C programból assembly hívása, példa #include <stdio.h> extern void nyomtat(char *); int main() { nyomtat("hello"); return 0; }
Assembly program, példa SEGMENT _TEXT PUBLIC CLASS=CODE USE16 ALIGN=16 _nyomtat: pushbp mov bp, sp pushax pushbx pushdx mov ah, 2h mov bx, [bp+4] call kiir pop dx pop bx pop ax pop bp ret...
Assembly program, példa... ; teljesség kedvéért kiir: mov dl,[bx] cmp dl, 0 jz vege int 21h inc bx jmp kiir vege: ret SEGMENT DATA
Kapcsolat C programmal C programban is definiálni kell, hogy a függvényünk hogyan néz ki Assembly-ben A függvény neve aláhúzással kezdődik Minden regisztert mentsünk el és állítsunk vissza Paraméterek a vermen
Címzés, egy kitérő Miért van szegmens:offset címzés? 16 bites processzor, max 65 535 byte címezhető, de több kellett Nem nagyobb regisztert terveztek Speciális címzés Speciális címzés Abszolút cím = szegmens * 16 + offset (16-al szorzás = egy nullát írunk a címhez)
Szegmens:offset F000:FFFD F0000 + FFFD FFFFD 923F:E2FF 923F0 + E2FF A06EF
Szegmens:offset A probléma, hogy ugyanaz a cím nagyon sokféleképpen írható le Mindegyik ugyanaz: 0007:7B90 0008:7B80 0009:7B70 000A:7B60 000B:7B50 000C:7B40 0047:7790 0048:7780 0049:7770 004A:7760 004B:7750 004C:7740 0077:7490 0078:7480 0079:7470 007A:7460 007B:7450 007C:7440 01FF:5C10 0200:5C00 0201:5BF0 0202:5BE0 0203:5BD0 0204:5BC0 07BB:0050 07BC:0040 07BD:0030 07BE:0020 07BF:0010 07C0:0000
Szegmens:offset Valójában van olyan memória cím, amit 4096 féle módon címezhetünk meg A szegmensek igazából csak képzeletbeli konstrukciók A szegmensek átfedik egymást A köztük levő különbség 16 byte
0000:0000 0001:0000 0002:0000 0003:0000 Szegmens:offset 16 byte 0000:000F 16 byte 0001:000F 16 byte 0002:000F 16 byte 0003:000F 0000:FFF0 0001:FFF0 0002:FFF0 0003:FFF0 Minden Szegmens 65 535 byte-ot tartalmaz 0000-FFFF 0000:FFFF 0001:FFFF 0002:FFFF 0003:FFFF
Egy kód szegmens van tiny, small, compact Memória modell függvények közel vannak egymáshoz csak az offset (IP) kerül a veremre (16 bites cím), CS változatlan CALL és RETN használható Kód szegmens neve: _TEXT Egy helyre kerülnek a függvények
Több kód szegmens van medium, large, huge Memória modell függvények távol vannak egymástól távoli függvényhívások kellenek CALL FAR CALL segment:offset RETF
Adat szegmens Egy 16 bites címek DS nem változik Több 32 bit írja le a címet Huge modell Memória modell A szegmens lehet 64 Kbyte-nál nagyobb A közös adatszegmens neve: _DATA
Tiny Memória modell code+adat <= 64K (COM program) Small code <= 64K, adat <= 64K Medium adat <= 64K, csak egy adat szegmens Compact code <= 64K, csak egy kód szegmesn
Large: Memória modell Több kód és adat szegmens Huge Egyedi tömb nagyobb lehet mint 64K Flat Nincs szegmens, 32 bites címek, védett mód
Szegmens definíció SEGMENT _TEXT PUBLIC CLASS=CODE USE16 ALIGN=16 PUBLIC Fordítási fázisban a linker ezeket összevonja CLASS=CODE Azonos osztályba tartozó szegmensek közel kerülnek egymáshoz USE16 16 bites assemblálási mód ALIGN=16 A szegmenst olyan címre kell illeszteni, amelyik osztható 16-al
PUBLIC Szegmens típus Összefűzi a szegmenseket COMMON overlay STACK Olyan mint PUBLIC SS-hez képest van relatív offset SP regisztert a végére állítja
Osztály (Class) Szegmens osztály Megadja hogy milyen sorrendben töltődjenek be a szegmensek Azonos nevűek egymás után töltődnek be Az osztály neve bármilyen szöveg lehet
Szegmens align Ha a cím 1-el osztható, byte alapú illesztés A szegmens közvetlenül a másik szegmens után következik Szegmens 1 Szegmens 2
Szegmens align Ha a cím 2-vel osztható, word alapú illesztés A szegmens páros címen kezdődik Szegmens 1 Szegmens 2 elpazarolt byte-ok, ha vannak
Szegmens align Ha a cím 4-vel osztható, dupla word alapú illesztés Elpazarolhatunk 1, 2 vagy 3 byte-ot Szegmens 1 Szegmens 2 elpazarolt byte-ok, ha vannak
Szegmens align Ha a cím 16-vel osztható, paragraph alapú illesztés Ez az alap eset 0-15 byte-ot lehet elpazarolni Szegmens 1 Szegmens 2 elpazarolt byte-ok, ha vannak
Szegmens align Hogyan lehet nem 16 bites szegmens illesztést megvalósítani? Átlapolással Például byte alapú illesztés: Előző szegmens vége: 10F87h Következő szegmens kezdete (byte aligned): 10F88h 10F90h Szegmens 1 Szegmens 2 10F80h=szegmens regiszter aktuális adat vagy kód itt kezdődik offset: 8
Sajnos Különböző assembler és C fordító különböző konvenciót használ Az előző példák esetén használt szoftver: NASM Turbo C 2.01 Ingyenes Kicsi 16-bites kódot generál!!! MINGW, DJGPP, CL fordítók esetén másképpen kellene eljárni
C és Assembly összefordítása Assembly fordítása nasm -fobj hello.asm C program fordítása tcc prog1.c hello.obj Alapeset Small memória modell Turbo C lefordítja és összelinkeli a két programot
Assembly-ből C függvény hívása global extern _main _printf SEGMENT _TEXT PUBLIC CLASS=CODE USE16 ALIGN=16 _main: push message call _printf add esp, 2 ret SEGMENT _DATA PUBLIC CLASS=DATA USE16 ALIGN=16 message: db 'Hello, World', 10, 0
Assembly-ből C függvény hívása _main A C program kezdőfüggvénye Paraméterátadás a szokásos módon Adatok külön szegmensben Assembly fordítása Linkelés nasm -fobj printf.asm tcc printf.obj
Paraméter átadás A paramétereket fordított sorrendben kell feltölteni a veremre Példa C nyelven: int myint = 1234; printf( A szam: %d, myint);
Paraméter átadás fordított sorrendben global extern _main _printf SEGMENT _TEXT PUBLIC CLASS=CODE USE16 ALIGN=16 _main: push word [myint] ; jobbról balra!!!!! push message call _printf add esp, 4 ret SEGMENT _DATA PUBLIC CLASS=DATA USE16 ALIGN=16 message db 'A szam: %d', 10, 0 myint dw 1234d
Függvény hívási konvenció Eddig a C hívási módot tárgyaltuk Bizonyos fordítók más konvenciót használnak A cdecl és stdcall hívási módok közötti különbség, hogy az stdcall esetén a szubrutin takarít maga után! stdcall : Pascal hívási mód Borland és Microsoft fordítók esetén: void _cdecl f(int)
Függvény hívási konvenció cdecl Előnye: Egyszerű és flexibilis Bármely C fordítóval használható Hátránya: Lassú lehet és sok memóriát használ stdcall Előnye: Nem kell takarítani, kevesebb hely Hátránya: Nem lehet változó paraméter számú függvénynél használni
Változók elérése Egy C programban deklarált változó: int i; Assembly-ben a hivatkozás extern _i mov ax,[_i]
Változók elérése Egy assembly programban deklarált változó: global _j _j dw 0 A C programban extern int j;