Assembly Rekurzív függvények, EXE, C programok Iványi Péter
Rekurzív függvény • 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
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 bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
org 100h mov ah, 02 push 04 call nyomtat add sp,2 int 20h
Rekurzív függvény 4 visszatérési cím
SP BP BX 0
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
4 visszatérési cím
SP BP BX 0
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
4 visszatérési cím BP tartalma SP BP BX 0
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
4 visszatérési cím BP tartalma SP BP BX 0
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 0
4 visszatérési cím BP tartalma BX tartalma DX tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 4
4 visszatérési cím BP tartalma BX tartalma DX tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 4
4 visszatérési cím BP tartalma BX tartalma DX tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím BP tartalma
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
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
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
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
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp És ret
SP BP BX 2
í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
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
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
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3 visszatérési cím
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 3
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
SP BP BX 4
4 visszatérési cím BP tartalma BX tartalma DX tartalma 3
Rekurzív függvény
nyomtat: push bp mov bp,sp push bx push dx mov bx, word [bp+4] mov dl,bl add dl, 30h; int 21h cmp bl, 0 jz nyomtat_vege dec bl push bx call nyomtat add sp,2 nyomtat_vege: pop dx pop bx pop bp ret
4 visszatérési cím
SP BP BX 4
org 100h
Rekurzív függvény 4
mov ah, 02 push 04 call nyomtat add sp,2 int 20h
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 programok • COM – Azonnal futtatható • 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 programok • EXE – 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 ? ex_Lpage dw ? ex_Fpages dw ?
; .exe signature [MZ vagy ZM] ; .exe file hossza mod 512 bytes < 512 bytes ; .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 mov mov mov mov int mov int mov int
ax, ds, es, dx, ah, 21h ah, 16h ah, 21h
data ; szegmens regiszter beállítása ax ax msg ; nyomtatandó szöveg 09h 0
; billentyű lenyomásra várunk
4ch
; kilépés
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
hello.obj assembly
program tárgy modul
hello.exe linking 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
EXE relokáció • Relokációs tábla: – 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 ... kiir: mov cmp jz int inc jmp vege: ret
; teljesség kedvéért dl,[bx] dl, 0 vege 21h bx kiir
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
0000:FFF0 0001:FFF0
Szegmens:offset 16 byte 16 byte 16 byte
0000:000F 0001:000F 0002:000F
16 byte
0003:000F
Minden Szegmens 65 535 byte-ot tartalmaz 0000-FFFF
0000:FFFF 0001:FFFF
0002:FFF0
0002:FFFF
0003:FFF0
0003:FFFF
Memória modell • Egy kód szegmens van – tiny, small, compact – 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
Memória modell • Több kód szegmens van – medium, large, huge – függvények távol vannak egymástól – távoli függvényhívások kellenek CALL FAR CALL segment:offset RETF
Memória modell • Adat szegmens – Egy • 16 bites címek • DS nem változik
– Több • 32 bit írja le a címet
• Huge modell – A szegmens lehet 64 Kbyte-nál nagyobb
• A közös adatszegmens neve: _DATA
Memória modell • Tiny – 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
Memória modell • Large: – 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
Szegmens típus • PUBLIC – Ö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
Szegmens osztály • Osztály (Class) – 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 „elpazarolt” byte-ok, ha vannak Szegmens 2
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 „elpazarolt” byte-ok, ha vannak Szegmens 2
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 Szegmens 1 10F90h 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 nasm -fobj printf.asm
• Linkelés 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;