Kezdőlap

|

Mi a kreditvadasz.hu Egy felsőoktatási közösségi oldal amely segít kapcsolatot tartani a hallgatók között, így segítséget nyújt a sikeres tanulmányokhoz...

Rodek Lajos-Diós Gábor - Assembly programozás

Országok listájaHungaryDebreceni EgyetemInformatikai KarProgramtervező informatikusSzámítógép ArchitektúrákEgyéb SegítségRodek Lajos-Diós Gábor - Assembly programozás

2009.01.06 14:48:35
(10)
Szerző: Simintoo
Cimkék: assembly, programozás, rodek lajos, diós gábor


Az alábbi szöveg egy formázás és képek nélküli előnézete a dokumentumnak. A tökéletes megjelenítéshez jelentkezz be, majd töltsd le a dokumentumot.

Assembly Programozás
Rodek Lajos Diós Gábor

Tartalomjegyzék
Ábrák jegyzéke Táblázatok jegyzéke El szó o Ajánlott irodalom 1. Az Assembly nyelv jelent sége o 2. A PC-k hardverének felépítése 3. Számrendszerek, gépi adatábrázolás 4. A 8086-os processzor jellemz i o 4.1. Memóriakezelés . . . . . . . . . . . . 4.2. Regiszterek . . . . . . . . . . . . . . 4.3. Adattípusok . . . . . . . . . . . . . . 4.4. Memóriahivatkozások, címzési módok 4.4.1. Közvetlen címzés . . . . . . 4.4.2. Báziscímzés . . . . . . . . . 4.4.3. Indexcímzés . . . . . . . . . 4.4.4. Bázis+relatív címzés . . . . 4.4.5. Index+relatív címzés . . . . 4.4.6. Bázis+index címzés . . . . . 4.4.7. Bázis+index+relatív címzés 4.5. Veremkezelés . . . . . . . . . . . . . 4.6. I/O, megszakítás-rendszer . . . . . . . 5. Az Assembly nyelv szerkezete, szintaxisa 6. A 8086-os processzor utasításkészlete 6.1. Prefixek . . . . . . . . . . . . . . . . . . 6.1.1. Szegmensfelülbíráló prefixek . . 6.1.2. Buszlezáró prefix . . . . . . . . 6.1.3. Sztringutasítást ismétl prefixek o 6.2. Utasítások . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . IV V VI VII 1 4 7 13 13 14 17 18 18 18 18 18 18 18 18 20 23 24 29 29 29 30 30 30

TARTALOMJEGYZÉK

II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 31 31 32 32 32 32 33 33 33 35 37 37 40 43 48 52 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 56 60 62 64 67 67 69 70 73 75 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 81 82 84 85 86 88 90 91 92 96 99

6.2.1. 6.2.2. 6.2.3. 6.2.4. 6.2.5. 6.2.6. 6.2.7. 6.2.8. 6.2.9. 6.2.10.

Adatmozgató utasítások . . . . . Egész számos aritmetika . . . . Bitenkénti logikai utasítások . . Bitléptet utasítások . . . . . . . o Sztringkezel utasítások . . . . o BCD aritmetika . . . . . . . . . Vezérlésátadó utasítások . . . . Rendszervezérl utasítások . . . o Koprocesszor-vezérl utasítások o Speciális utasítások . . . . . . .

7. Assembly programok készítése 8. Vezérlési szerkezetek megvalósítása 8.1. Szekvenciális vezérlési szerkezet 8.2. Számlálásos ismétléses vezérlés 8.3. Szelekciós vezérlés . . . . . . . 8.4. Eljárásvezérlés . . . . . . . . . 9. A Turbo Debugger használata 10. Számolás el jeles számokkal o 10.1. Matematikai kifejezések kiértékelése 10.2. BCD aritmetika . . . . . . . . . . . 10.3. Bitforgató utasítások . . . . . . . . 10.4. Bitmanipuláló utasítások . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

11. Az Assembly és a magas szintu nyelvek 11.1. Paraméterek átadása regisztereken keresztül 11.2. Paraméterek átadása globálisan . . . . . . . 11.3. Paraméterek átadása a vermen keresztül . . 11.4. Lokális változók megvalósítása . . . . . . . 12. Muveletek sztringekkel 13. Az .EXE és a .COM programok, a PSP 13.1. A DOS memóriakezelése . . . . . . 13.2. Általában egy programról . . . . . . 13.3. Ahogy a DOS indítja a programokat 13.4. .COM állományok . . . . . . . . . . 13.5. Relokáció . . . . . . . . . . . . . . 13.6. .EXE állományok . . . . . . . . . . 14. Szoftver-megszakítások 14.1. Szövegkiíratás, billenty zet-kezelés u 14.2. Szöveges képerny kezelése . . . . o 14.3. Munka állományokkal . . . . . . . 14.4. Grafikus funkciók használata . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

TARTALOMJEGYZÉK

III 102 103 107 111 114 116 120 125 130

15. Hardver-megszakítások, rezidens program 15.1. Szoftver-megszakítás átirányítása . . . . . . . . . . . . . . . . . . . . . . . . 15.2. Az id zít (timer) programozása . . . . . . . . . . . . . . . . . . . . . . . . o o 15.3. Rezidens program készítése . . . . . . . . . . . . . . . . . . . . . . . . . . . 16. Kivételek A. Átváltás különféle számrendszerek között B. Karakter kódtáblázatok Tárgymutató Irodalomjegyzék

Ábrák jegyzéke
2.1. 3.1. 3.2. 3.3. 3.4. 4.1. 4.2. 9.1. A PC-k felépítése . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . A NOT m velet igazságtáblája . u Az AND m velet igazságtáblája u Az OR m velet igazságtáblája . u A XOR m velet igazságtáblája . u . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 8 8 8 9 16 16 54

Az AX regiszter szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . . A Flags regiszter szerkezete . . . . . . . . . . . . . . . . . . . . . . . . . . A Turbo Debugger képerny je . . . . . . . . . . . . . . . . . . . . . . . . . o

Táblázatok jegyzéke
8.1. 8.2. 8.3. 14.1. 14.2. 14.3. 14.4. El jeles aritmetikai feltételes ugrások . . . . . . . . . . . . . . . . . . . . . o El jeltelen aritmetikai feltételes ugrások . . . . . . . . . . . . . . . . . . . . o Speciális feltételes ugrások . . . . . . . . . . . . . . . . . . . . . . . . . . . Gyakran használt szoftver-megszakítások . . . Állomány- és lemezkezel DOS-szolgáltatások o Standard I/O eszközök handle értéke . . . . . . Színkódok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 45 46

. 91 . 98 . 98 . 101

16.1. A 8086-os processzoron létez kivételek . . . . . . . . . . . . . . . . . . . . 115 o A.1. B.1. B.2. Átváltás a 2-es, 8-as, 10-es és 16-os számrendszerek között . . . . . . . . . . 116 ASCII és EBCDIC vezérl kódok . . . . . . . . . . . . . . . . . . . . . . . . 120 o ASCII és EBCDIC karakterkódok . . . . . . . . . . . . . . . . . . . . . . . 121

El szó o
Valami . . .

Ajánlott irodalom
A következ lista tartalmazza azon m vek adatait, amelyek elolvasása el segít(het)i a jegyo u o zet könnyebb megértését: 1. Peth Ádám: Assembly alapismeretek 1. kötet, Számalk, Budapest, 1992 o 2. Peter Norton ­ John Socha: Az IBM PC Assembly nyelv programozása, Novotrade, Buu dapest, 1991 3. Peter Norton: Az IBM PC programozása, M szaki Könyvkiadó, Budapest, 1992 u 4. László József: A VGA-kártya programozása Pascal és Assembly nyelven, ComputerBooks, Budapest, 1994 5. Abonyi Zsolt: PC hardver kézikönyv 6. Dr. Kovács Magda: 32 bites mikroprocesszorok 80386/80486 I. és II. kötet, LSI, Budapest Az (1), (2) és (3) könyvek a kezd knek, az Assemblyvel most ismerked knek valók. o o A (4) és (5) könyvek a hardverrel foglalkoznak, az Assemblyt ezekb l nem fogjuk megtao nulni. Végül a (6) egy referenciakönyv, így ezt f leg az Assemblyben már jártas, de mélyebb o ismeretekre vágyóknak ajánljuk.

1. fejezet

Az Assembly nyelv tulajdonságai, jelent sége o
A számítógépes problémamegoldás során a kit zött célt megvalósító algoritmust mindig vau lamilyen programozási nyelven (programming language) írjuk, kódoljuk le. A nyelvet sokszor az adott feladat alapján választjuk meg, míg máskor aszerint döntünk egy adott nyelv mellett, hogy az hozzánk, az emberi gondolkodáshoz mennyire áll közel. Ez utóbbi tulajdonság alapján csoportosíthatók a számítógépes programozási nyelvek: megkülönböztetünk alacsony szintu (low-level) és magas szintu programozási nyelveket (high-level programming language). Az el bbire jó példák az Assembly és részben a C, az utóbbira pedig a Pascal ill. a BASIC nyelvek. o Ha a nyelvek szolgáltatásait tekintjük, akkor szembeötl , hogy ahogy egyre fentebb haladunk o az alacsony szint nyelvekt l a magas szint ek felé, úgy egyre nagyobb szabadsággal, egyre u o u általánosabb megoldásokkal találkozunk. Az Assembly tehát egy alacsony szint programozási nyelv, méghozzá nagyon alacsony u szint , ebb l következ en pedig sokkal közelebb áll a hardverhez, mint bármely más nyelv. u o o F bb jellemz i: o o · nagyon egyszer , elemi m veletek u u · típustalanság · rögzített utasításkészlet · világos, egyszer szintaxis u · kevés vezérlési szerkezet · nagyon kevés adattípus; ha több is van, akkor általában egymásból származtathatók valahogyan De miért is van szükség az Assemblyre, ha egyszer ott van a többi nyelv, amikben jóval kényelmesebben programozhatunk? Erre egyik indok, hogy a magas szint nyelvek eljárásai, u függvényei sokszor általánosra lettek megírva, így teljesen feleslegesen foglalkoznak olyan dolgokkal, amikre esetleg soha sem lesz szükségünk. Erre jó példák lehetnek a Borland Pascal/C grafikus eljárásai, valamint ki-/bemeneti (I/O) szolgáltatásai. Kört rajzolhatunk a Circle eljárással is, de ennél gyorsabb megoldást kapunk, ha vesszük a fáradságot, és mi magunk írunk

2 egy olyan körrajzolót, ami semmi mást nem csinál, csak ami a feladata: helyesen kirajzolja a kört a képerny re, de nem foglalkozik pl. hibaellen rzéssel, a képerny szélén kívülre kerül o o o o pontok kisz résével stb. Hasonló a helyzet a fájlkezeléssel is. Ha nem akarunk speciális típusou kat (mondjuk objektumokat, rekordokat) állományba írni, mindössze valahány bájtot szeretnénk beolvasni vagy kiírni a lemezre, akkor felesleges a fenti nyelvek rutinjait használni. Mindkét feladat megoldható Assemblyben is, méghozzá hatékonyabban, mint a másik két nyelvben. Akkor miért használják mégis többen a C-t, mint az Assemblyt? A választ nem nehéz megadni: magasabb szint nyelvekben a legtöbb probléma gyorsabban leírható, a forrás rövidebb, u strukturáltabb, s ezáltal áttekinthet bb lesz, könnyebb lesz a kés bbiekben a program karbantaro o tása, és ez nem csak a program szerz jére vonatkozik. Mégsem mell zhetjük az Assemblyt, sok o o dolgot ugyanis vagy nagyon nehéz, vagy egyszer en képtelenség megcsinálni más nyelvekben, u míg Assemblyben némi energia befektetése árán ezek is megoldhatók. Aki Assemblyben akar programozni, annak nagyon elszántnak, türelmesnek, kitartónak kell lennie. A hibalehet ségek o ugyanis sokkal gyakoribbak itt, és egy-egy ilyen baki megkeresése sokszor van olyan nehéz, mint egy másik program megírása. Régen, mikor még nem voltak modern programozási nyelvek, fordítóprogramok, akkor is kellett valahogy dolgozni az embereknek. Ez egy gépi kódnak (machine code) nevezett nyelven történt. A gépi kód a processzor saját nyelve, csak és kizárólag ezt érti meg. Ez volt ám a fárasztó dolog! A gépi kód ugyanis nem más, mint egy rakás szám egymás után írva. Akinek ebben kellett programozni, annak fejb l tudnia kellett az összes utasítás összes lehetséges o változatát, ismernie kellett a rendszert teljes mértékben. Ha egy kívülálló rátekintett egy gépi kódú programra, akkor annak m ködéséb l, jelentéséb l jobbára semmit sem értett meg. Nézu o o zünk egy példát: 0B8h 34h 12h // 0F7h 26h 78h 56h // 0A3h 78h 56h, ahol a dupla törtvonal az utasításhatárt jelzi. Ez a tíz hexadecimális (tizenhatos számrendszerbeli) szám nem mond túl sokat els ránézésre. Éppen ezért kidolgoztak egy olyan jelölésrendszert, nyelvet, amiben emo berileg emészthet formában leírható bármely gépi kódú program. Ebben a nyelvben a hasonló o folyamatot végrehajtó gépi kódú utasítások csoportját egyetlen szóval, az ú.n. mnemonikkal (mnemonic) azonosítják. Természetesen van olyan mnemonik is, ami egyetlen egy utasításra vonatkozik. Ez a nyelv lett az Assembly. Ebben az új jelölésrendszerben a fenti programrészlet a következ formát ölti: o
MOV MUL MOV AX,1234h WORD PTR [5678h] [5678h],AX ;0B8h 34h 12h ;0F7h 26h 78h 56h ;0A3h 78h 56h

Némi magyarázat a programhoz: · az els sor egy számot (1234h) rak be az AX regiszterbe, amit most tekinthetünk mondjuk o egy speciális változónak · a második sor a fenti értéket megszorozza a memória egy adott címén (5678h) található értékkel · a harmadik sor az eredményt berakja az el bbi memóriarekeszbe o · a pontosvessz utáni rész csak megjegyzés o · az els oszlop tartalmazza a mnemonikot o · a memóriahivatkozásokat szögletes zárójelek ( [ és ] ) közé írjuk

3 Tehát a fenti három sor egy változó tartalmát megszorozza az 1234h számmal, és az eredményt visszarakja az el bbi változóba. o Mik az Assembly el nyei? o · korlátlan hozzáférésünk van a teljes hardverhez, beleértve az összes perifériát (billenty u zet, nyomtató stb.) · pontosan ellen rizhetjük, hogy a gép tényleg azt teszi-e, amit elvárunk t le o o · ha szükséges, akkor minimalizálhatjuk a program méretét és/vagy sebességét is (ez az ú.n. optimalizálás) Most lássuk a hátrányait: · a forrás sokszor áttekinthetetlen még a szerz nek is o · a kódolás nagy figyelmet, türelmet, és f leg id t igényel o o · sok a hibalehet ség o · a hardver alapos ismerete elengedhetetlen · a forrás nem hordozható (nem portolható), azaz más alapokra épül számítógépre átírás o nélkül nem vihet át (ez persze igaz a gépi kódra is) o Bár úgy t nhet, több hátránya van mint el nye, mégis érdemes alkalmazni az Assemblyt ott, u o ahol más eszköz nem segít. A befektetett er feszítések pedig meg fognak térülni. o

2. fejezet

A PC-k hardverének felépítése
Az IBM PC-k felépítését szemlélteti a 2.1. ábra eléggé leegyszer sítve: u

MEMÓRIA
CÍM
ADAT

PORTOK

CPU
REGISZTEREK
MEGSZAKÍTÁSKÉRELEM

PERIFÉRIÁK
2.1. ábra. A PC-k felépítése Az els IBM PC az Intel 8086-os mikroprocesszorával jelent meg, és hamarosan követte o az IBM PC XT, ami már Intel 8088-os maggal ketyegett. Kés bb beköszöntött az AT-k id o o szaka, s vele jöttek újabb processzorok is: Intel 80286, 80386 (SX és DX), 80486 (SX, DX, DX2 és DX4), majd eljött az 586-os és 686-os gépek világa (Pentium, Pentium Pro, Pentium II stb.) Nem csak az Intel gyárt processzorokat PC-kbe, de az összes többi gyártó termékére jellemz , hogy (elvileg) 100%-osan kompatibilis az Intel gyártmányokkal, azaz ami fut Intel-en, o az ugyanúgy elfut a másik procin is, és viszont. Az összes kés bbi processzor alapja tulajdono képpen a 8086-os volt, éppen ezért mondják azt, hogy a processzorok ezen családja az Intel

5 80x86-os (röviden x86-os) architektúrájára épül. A továbbiakban kizárólag az Intel 8086/8088-os processzorok által biztosított programozási környezetet vizsgáljuk. (A két proci majdnem teljesen megegyezik, a különbség mindössze az adatbuszuk szélessége: a 8086 16 bites, míg a 8088-as 8 bites küls adatbusszal rendelkezik.) o A 2.1. ábrán a CPU jelöli a processzort (Central Processing Unit ­ központi feldolgozó egy ség). Mint neve is mutatja, o a gép agya, de persze gondolkodni nem tud, csak végrehajtja, amit parancsba adtak neki. A CPU-n többek között található néhány különleges, közvetlenül elérhet o tárolóhely. Ezeket regisztereknek (register) nevezzük. A processzor m ködés közbeni újrainu dítását resetnek hívjuk. (Az angol ,,reset" szó egyik magyar jelentése az ,,utánállít, beállít", de a számítástechnikában általában ,,újraindításként" fordítják.) Reset esetén a processzor egy jól meghatározott állapotba kerül (pl. minden regiszterbe valamilyen rögzített érték lesz betöltve). A számítógép bekapcsolásakor is lezajlik a reset. Az újraindítás egy finomabb fajtája az inicializálás vagy röviden init (initialization, init). Az init során néhány regiszter értéke meg rzésre o kerül, a reset-t l eltér en. o o A memória adja a számítógép ,,emlékezetét". Hasonlóan az emberi memóriához, van neki felejt (az információt csak bizonyos ideig meg rz ) és emlékez változata. Az el bbi neve o o o o o RAM (Random Access Memory ­ véletlen hozzáférés memória), míg az utóbbié ROM (Read u Only Memory ­ csak olvasható memória). A ROM fontos részét képezi az ú.n. BIOS (Basic Input/Output System ­ alapvet ki-/bemeneti rendszer). A gép bekapcsolása (és reset) után a o BIOS-ban lev egyik fontos program indul el el ször (pontosabban minden processzort úgy o o terveznek, hogy a BIOS-t kezdje el végrehajtani ilyenkor). Ez leellen rzi a hardverelemeket, o teszteli a memóriát, megkeresi a jelenlev perifériákat, majd elindítja az operációs rendszert, ha o lehet. A BIOS ezenkívül sok hasznos rutint tartalmaz, amikkel vezérelhetjük például a billenty zetet, videokártyát, merevlemezt stb. u Busznak (bus) nevezzük ,,vezetékek" egy csoportját, amik bizonyos speciális célt szolgálnak, és a CPU-t kötik össze a számítógép többi fontos részével. Megkülönböztetünk adat-, címill. vezérl buszt (data bus, address bus, control bus). A címbusz szélessége (amit bitekben ill. a o ,,vezetékek" számában mérünk) határozza meg a megcímezhet memória maximális nagyságát. o A CPU a perifériákkal (pl. hangkártya, videovezérl , DMA-vezérl , nyomtató stb.) az o o ú.n. portokon keresztül kommunikál. (Tekinthetjük oket egyfajta ,,átjárónak" is.) Ezeket egy szám azonosítja, de ne úgy képzeljük el, hogy annyi vezeték van bekötve, ahány port van. Egyszer en, ha egy eszközt el akar érni a CPU, akkor kiírja a címbuszára a port számát, és ha u ott ,,van" eszköz (tehát egy eszköz arra van beállítva, hogy erre a portszámra reagáljon), akkor az válaszol neki, és a kommunikáció megkezd dik. o Azonban nem csak a CPU szólhat valamelyik eszközhöz, de azok is jelezhetik, hogy valami mondanivalójuk van. Erre szolgál a megszakítás-rendszer (interrupt system). Ha a CPU érzékel egy megszakítás-kérelmet (IRQ ­ Interrupt ReQuest), akkor abbahagyja az éppen aktuális munkáját, és kiszolgálja az adott eszközt. A megszakítások el ször a megszakítás-vezérl höz o o (interrupt controller) futnak be, s csak onnan mennek tovább a processzorhoz. Az XT-k 8, az AT-k 16 db. független megszakítás-vonallal rendelkeznek, azaz ennyi perifériának van lehet sége a megszakítás-kérésre. Ha egy periféria használ egy megszakítás-vonalat, akkor azt o kizárólagosan birtokolja, tehát más eszköz nem kérhet megszakítást ugyanazon a vonalon. A fentebb felsorolt rendszerelemek (CPU, memória, megszakítás-vezérl , buszok stb.) és o még sok minden más egyetlen áramköri egységen, az ú.n. alaplapon (motherboard) található. Van még három fontos eszköz, amikr l érdemes szót ejteni. Ezek az órajel-generátor, az o id zít és a DMA-vezérl . o o o A CPU és a perifériák m ködését szabályos id közönként megjelen elektromos impulu o o zusok vezérlik. Ezeket nevezik órajelnek (clock, clocktick), másodpercenkénti darabszámuk

6 mértékegysége a Hertz (Hz). Így egy 4.77 MHz-es órajel másodpercenként 4770000 impulzust jelent. Az órajelet egy kvarckristályon alapuló órajel-generátor (clock generator) állítja el . o A RAM memóriák minden egyes memóriarekeszét folyamatosan ki kell olvasni és vissza kell írni másodpercenként többször is, különben tényleg ,,felejt vé" válna. Erre a frissítésnek o (memory refresh) nevezett m veletre felépítésük miatt van szükség. (A korrektség kedvéért: ez u csak az ú.n. dinamikus RAM-okra, avagy DRAM-okra igaz.) A m veletet pontos id közönként u o kell végrehajtani, ezt pedig egy különleges egység, az id zít (timer) intézi el. Ennek egyik o o dolga az, hogy kb. 15.09 µs-onként elindítsa a frissítést (ez nagyjából 66287 db. frissítést jelent másodpercenként). Ezenkívül a rendszerórát (system clock) is ez a szerkezet szinkronizálja. A memória elérése a CPU-n keresztül igen lassú tud lenni, ráadásul erre az id re a proo cesszor nem tud mással foglalkozni. E célból bevezették a közvetlen memória-hozzáférés (DMA ­ Direct Memory Access) módszerét. Ez úgy m ködik, hogy ha egy perifériának szüku sége van valamilyen adatra a memóriából, vagy szeretne valamit beírni oda, akkor nem a CPUnak szól, hanem a DMA-vezérl nek (DMA controller), és az a processzort kikerülve elintézi a o kérést.

3. fejezet

Számrendszerek, gépi adatábrázolás, aritmetika és logika
Az emberek általában tízes (decimális ­ decimal) számrendszerben számolnak a mindennapjaik során, hiszen ezt tanították nekik, és ez az elfogadott konvenció a világon. A processzort azonban (de a többi hardverösszetev t, pl. a memóriát is) feleslegesen túlbonyolítaná, ha neki o is ezekkel a számokkal kellene dolgoznia. Ennél jóval egyszer bb és kézenfekv megoldás, ha u o kettes alapú (bináris ­ binary) számrendszerben kezel minden adatot. Az információ alapegysége így a bináris számjegy, a bit (BInary digiT) lesz, ezek pedig a 0 és az 1. Bináris számok ábrázolásakor ugyanúgy helyiértékes felírást használunk, mint a decimális számok esetén. Pl. a 10011101 bináris számnak 128 + 16 + 8 + 4 + 1 = 157 az értéke. Az egyes helyiértékek jobbról balra 2-nek egymás után következ hatványai, tehát 1, 2, 4, 8, 16, 32 stb. Ha sokszor dolgozunk o bináris számokkal, akkor nem árt, ha a hatványokat fejb l tudjuk a 0-diktól a 16-odikig. o Egy-egy aránylag kicsi szám bináris leírásához sok 0-t és 1-et kell egymás mellé raknunk, ez pedig néha fárasztó. Ezt kiküszöbölend a számokat sokszor írjuk tizenhatos alapú (hexadecimális ­ o hexadecimal) számrendszerben. Itt a számjegyek a megszokott 10 arab számjegy, plusz az angol (latin) ábécé els hat bet je (A, B, C, D, E, F), továbbá A = 10, B = 11 stb. Mivel 16 = 24 , o u ezért négy bináris számjegy éppen egy hexadecimális (röviden hexa) számjegyet tesz ki. Az el z példa alapján 10011101b = 9Dh = 157d. Ahhoz, hogy mindig tudjuk, a leírt számot mio o lyen alapú rendszerben kell értelmezni, a szám után írunk egy ,,b", ,,h" vagy ,,d" bet t. Ha nem u jelöljük külön, akkor általában a tízes számrendszert használjuk. Szokás még néha a nyolcas alapú (oktális ­ octal) felírást is alkalmazni. Ekkor a 0 ­ 7 számjegyeket használjuk, és 3 bináris jegy tesz ki egy oktális számjegyet. Az oktális számok végére ,,o" bet t írunk. u Most elevenítsük fel a legegyszer bb, közismert logikai m veleteket. Ezek ugyanis fontos u u szerepet játszanak mind a programozás, mind a processzor szempontjából. A két logikai igazságértéket itt most bináris számjegyek fogják jelölni. Megszokott dolog, hogy 1 jelenti az ,,igaz" értéket. A negáció (tagadás ­ negation) egyváltozós (unáris) m velet, eredménye a bemeneti igazu ságérték ellentettje. A m veletet jelölje NOT az angol tagadás mintájára. Hatása a 3.1. ábrán u látható. A konjunkció (,,ÉS") már kétváltozós (bináris) m velet. Jele AND (az ,,és" angolul), eredu ményét a 3.2. ábra szemlélteti:

8 NOT 0 1

1 0

3.1. ábra. A NOT m velet igazságtáblája u AND 0 1 0 0 0 1 0 1

3.2. ábra. Az AND m velet igazságtáblája u A diszjunkció (,,VAGY") szintén bináris m velet. Jele OR (a ,,vagy" angolul), és a két u változón MEGENGEDO VAGY m veletet hajt végre. Igazságtáblája a 3.3. ábrán látható. u OR 0 1 0 0 1 1 1 1

3.3. ábra. Az OR m velet igazságtáblája u Utolsó m veletünk az antivalencia (,,KIZÁRÓ VAGY", az ekvivalencia tagadása). Jele az u XOR (eXclusive OR), hatása a 3.4. ábrán követhet . o A legtöbb processzor kizárólag egész számokkal tud számolni, esetleg megenged racionális (valós) értékeket is. Az ábrázolható számok tartománya mindkét esetben véges, ezt ugyanis a processzor regisztereinek bitszélessége határozza meg. A számítástechnikában a legkisebb ábrázolható információt a bit képviseli, de mindenhol az ennél nagyobb, egészen pontosan 8 db. bitb l álló bájtot (byte) használják az adatok alapegységeként, és pl. a regiszterek és az adatbusz o szélessége is ennek többszöröse. Szokás még más, a bájt fogalmára épül mértékegységet is o használni, ilyen a szó (word; általában 2 vagy 4 bájt), a duplaszó (doubleword; a szó méretének kétszerese) és a kvadraszó (quadword; két duplaszó méret ). A bájt alsó ill. fels felének (4 u o bitjének) neve nibble (ez egy angol kifejezés, és nincs magyar megfelel je). Így beszélhetünk o alsó és fels nibble-r l. A bájtban a biteket a leírás szerint jobbról balra 0-tól kezdve számozzák, o o és egy bájtot két hexadecimális számjeggyel lehet leírni. A számítástechnikában a kilo- (k, K) és mega- (M) el tétszavak a megszokott 1000 és o 1000000 helyett 1024-et (= 210 ) ill. 1048576-ot (= 220 ) jelentenek. A giga- (G), tera- (T), peta- (P) és exa- (E) hasonlóan 230 -t, 240 -t, 250 -t ill. 260 -t jelentenek. Fontos szólni egy fogalomról, az ú.n. endianizmusról (endianism). Ez azt a problémát jelenti, hogy nincs egyértelm en rögzítve a több bájt hosszú adatok ábrázolása során az egyes u bájtok memóriabeli sorrendje. Két logikus verzió létezik: a legkevésbé értékes bájttal kezdünk, és a memóriacím növekedésével sorban haladunk a legértékesebb bájt felé (little-endian tárolás), ill. ennek a fordítottja, tehát a legértékesebbt l haladunk a legkevésbé értékes bájt felé o (big-endian tárolás). Mindkét megoldásra találhatunk példákat a különböz hardvereken. o Nézzük meg, hogy ábrázoljuk az egész számokat. El ször tételezzük fel, hogy csak nemneo gatív (el jeltelen ­ unsigned) számaink vannak, és 1 bájtot használunk a felíráshoz. Nyolc biten o 0 és 255 (= 28 - 1) között bármilyen szám felírható, ezért az ilyen számokkal nincs gond.

9 XOR 0 1 0 0 1 1 1 0

3.4. ábra. A XOR m velet igazságtáblája u Ha negatív számokat is szeretnénk használni, akkor két lehet ség adódik: o · csak negatív számokat ábrázolunk · az ábrázolási tartományt kiterjesztjük a nemnegatív számokra is Ez utóbbi módszert szokták választani, és a tartományt praktikus módon úgy határozzák meg, hogy a pozitív és negatív számok nagyjából azonos mennyiségben legyenek. Ez esetünkben azt jelenti, hogy -128-tól +127-ig tudunk számokat felírni (beleértve a 0-t is). De hogy különböztessük meg a negatív számokat a pozitívaktól? Természetesen az el jel (sign) által. Mivel ennek o két értéke lehet (ha a nullát pozitívnak tekintjük), tárolására elég egyetlen bit. Ez a kitüntetett bit az el jelbit (sign bit), és megegyezés szerint az adat legértékesebb (most significant), azaz o legfels bitjén helyezkedik el. Ha értéke 0, akkor a tekintett el jeles (signed) szám pozitív (vagy o o nulla), 1 esetén pedig negatív. A fennmaradó biteken (esetünkben az alsó 7 bit) pedig tároljuk magát a számot el jele nélkül. Megtehetnénk, hogy azonos módon kezeljük a pozitív és negatív o számokat is, de kényelmi szempontok és a matematikai m veleti tulajdonságok fenntartása véu gett a negatív számok más alakban kerülnek leírásra. Ez az alak az ú.n. kettes komplemens (2's complement). Ennek kiszámítása majdnem triviális, egyszer en ki kell vonni a számot a 0-ból. u (Ehhez persze ismerni kell a kivonás szabályait, amiket alább ismertetünk.) Egy másik módszerhez vezessük be az egyes komplemens (1's complement) fogalmát is. Ezt úgy képezzük bármilyen érték bájt (szó stb.) esetén, hogy annak minden egyes bitjét negáljuk (invertáljuk). u Ha vesszük egy tetsz leges szám egyes komplemensét, majd ahhoz hozzáadunk 1-et (az összeao dást az alábbiak szerint elvégezve), akkor pont az adott szám kettes komplemensét kapjuk meg. Egy szám kettes komplemensének kettes komplemense a kiinduló számot adja vissza. Ilyen módon a kettes komplemens képzése ekvivalens a szám -1-szeresének meghatározásával. Egy példán illusztrálva: legyenek az ábrázolandó számok 0, 1, 2, 127, 128, 255, -1, -2, -127 és -128. A számok leírása ekkor így történik: · 0 (el jeltelen vagy el jeles pozitív) = 00000000b o o · 1 (el jeltelen vagy el jeles pozitív) = 00000001b o o · 2 (el jeltelen vagy el jeles pozitív) = 00000010b o o · 127 (el jeltelen vagy el jeles pozitív) = 01111111b o o · 128 (el jeltelen) = 10000000b o · 255 (el jeltelen) = 11111111b o · -1 (el jeles negatív) = 11111111b o · -2 (el jeles negatív) = 11111110b o · -127 (el jeles negatív) = 10000001b o

10 · -128 (el jeles negatív) = 10000000b o Láthatjuk, hogy az el jeltelen és el jeles ábrázolás tartományai között átfedés van (0 ­ 127), o o míg más értékek esetén ütközés áll fenn (128 ­ 255 ill. -1 ­ -128). Azaz a 11111111b számot olvashatjuk 255-nek de akár -1-nek is! Ha nem bájton, hanem mondjuk 2 bájtos szóban akarjuk tárolni a fenti számokat, akkor ezt így tehetjük meg: · 0 = 00000000 00000000b · 1 = 00000000 00000001b · 2 = 00000000 00000010b · 127 = 00000000 01111111b · 128 = 00000000 10000000b · 255 = 00000000 11111111b · -1 = 11111111 11111111b · -2 = 11111111 11111110b · -127 = 11111111 10000001b · -128 = 11111111 10000000b Ebben az esetben meg tudjuk különböztetni egymástól a -1-et és a 255-öt, de ütköz rész itt is o van (32768 ­ 65535 ill. -1 ­ -32768). Végül megnézzük, hogy végezhet k el a legegyszer bb matematikai m veletek. A m veo u u u letek közös tulajdonsága, hogy az eredmény mindig hosszabb 1 bittel, mint a két szám közös hossza (úgy tekintjük, hogy mindkett tag azonos bitszélesség ). Így két 1 bites szám összege o u és különbsége egyaránt 2 bites, míg két bájté 9 bites lesz. A +1 bit tulajdonképpen az esetleges átvitelt tárolja. Két bináris számot ugyanúgy adunk össze, mint két decimális értéket: 1. kiindulási pozíció a legalacsonyabb helyiérték jegy, innen haladunk balra u 2. az átvitel kezdetben 0 3. az aktuális pozícióban lev két számjegyet összeadjuk, majd ehhez hozzáadjuk az el z o o o átvitelt (az eredmény két jegy bináris szám lesz) u 4. a kétbites eredmény alsó jegyét leírjuk az összeghez, az átvitel pedig felveszi a fels o számjegy értékét 5. ha még nem értünk végig a két összeadandón, akkor menjünk ismét a (3)-ra 6. az átvitelt mint számjegyet írjuk hozzá az összeghez Az összeadási szabályok pedig a következ ek: o · 0 + 0 = 00 (számjegy = 0, átvitel = 0)

11 · 0 + 1 = 01 (számjegy = 1, átvitel = 0) · 1 + 0 = 01 (számjegy = 1, átvitel = 0) · 1 + 1 = 10 (számjegy = 0, átvitel = 1) · 10 + 01 = 11 (számjegy = 1, átvitel = 1) Ezek alapján ellen rizhetjük, hogy a fent definiált kettes komplemens alak valóban teljesíti o azt az alapvet algebrai tulajdonságot, hogy egy számnak és additív inverzének (tehát -1o szeresének) összege 0 kell legyen. És valóban: 1d + (-1d) = 00000001b + 11111111b = 1 00000000b Az eredmény szintén egy bájt lesz (ami tényleg nulla), valamint keletkezik egy átvitel is. Az egyes komplemens is rendelkezik egy érdekes tulajdonsággal. Nevezetesen, ha összeadunk egy számot és annak egyes komplemensét, akkor egy csupa egyesekb l álló számot, azaz -1-et o (avagy a legnagyobb el jeltelen számot) fogunk kapni! o Kivonáskor hasonlóan járunk el, csak más szabályokat alkalmazunk: · 0 - 0 = 00 (számjegy = 0, átvitel = 0) · 0 - 1 = 11 (számjegy = 1, átvitel = 1) · 1 - 0 = 01 (számjegy = 1, átvitel = 0) · 1 - 1 = 00 (számjegy = 0, átvitel = 0) · 11 - 01 = 10 (számjegy = 0, átvitel = 1) továbbá a fenti algoritmusban a (3) lépésben az átvitelt le kell vonni a két számjegy különbségéb l. Ezek alapján ugyancsak teljesül, hogy o 1d - 1d = 00000001b - 00000001b = 0 00000000b azaz egy számot önmagából kivonva 0-t kapunk. Viszont lényeges eltérés az el z esett l (amio o o kor a kettes komplemens alakot adtuk hozzá a számhoz), hogy itt sose keletkezik átvitel a m u velet elvégzése után. A kivonás szabályainak ismeretében már ellen rizhet , hogy a kettes komplemenst valóban o o helyesen definiáltuk: 00000000b - 00000001b = 1 11111111b A szorzás és az osztás már macerásabbak. Mindkét m velet elvégzése el tt meghatározzuk u o az eredmény (szorzat ill. hányados) el jelét, majd az el jeleket leválasztjuk a tagokról. Mindkét o o m veletet ezután ugyanúgy végezzük, mint ahogy papíron is csinálnánk. Osztás alatt itt marau dékos egész osztást értünk. A szorzat hossza a kiinduló tagok hosszainak összege, a hányadosé az osztandó és osztó hosszainak különbsége, míg a maradék az osztandó hosszát örökli. (Ezt azért nem kell szigorúan venni. Egy szót egy bájttal elosztva a hányados nyudodtan hosszabb lehet 8 bitnél. Pl.: 0100h/01h = 0100.) A maradék el jele az osztandóéval egyezik meg, továbbá o teljesül, hogy a maradék abszolút értékben kisebb mint az osztó abszolút értéke.

12 A m veletek egy speciális csoportját alkotják a 2 hatványaival való szorzás és osztás. Ezeket u közös néven shiftelésnek (eltolásnak, léptetésnek) hívjuk. 2-vel úgy szorozhatunk meg egy számot a legegyszer bben, ha a bináris alakban utána írunk u egy 0-t. De ez megegyezik azzal az esettel, hogy minden számjegy eggyel magasabb helyiértékre csúszik át, az alsó, üresen maradó helyet pedig egy 0-val töltjük ki. Erre azt mondjuk, hogy a számot egyszer balra shifteltük. Ahányszor balra shiftelünk egy számot, mindannyiszor megszorozzuk 2-vel, végeredményben tehát 2-nek valamely hatványával szorozzuk meg. Ez a módszer minden számra alkalmazható, legyen az akár negatív, akár pozitív. Hasonlóan definiálható a jobbra shiftelés is, csak itt a legfels megüresed bitpozíciót tölto o jük fel 0-val. Itt azonban felmerül egy bökken , nevezetesen negatív számokra nem fogunk o helyes eredményt kapni. A probléma az el jelbitben keresend , mivel az éppen a legfels bit, o o o amit pedig az el bb 0-val helyettesítettünk. A gond megszüntethet , ha bevezetünk egy el jeles o o o és egy el jel nélküli jobbra shiftelést. Az el jeltelen változat hasonlóan m ködik a balra shifteo o u léshez, az el jelesnél pedig annyi a változás, hogy a legfels bitet változatlanul hagyjuk (vagy o o ami ugyanaz, az eredeti el jelbittel töltjük fel). o Megjegyezzük, hogy az angol terminológia megkülönbözteti az összeadáskor keletkez áto vitelt a kivonásnál keletkez t l. Az el bbit carry-nek, míg az utóbbit borrow-nak nevezik. oo o Túlcsordulásról (overflow) beszélünk, ha a m velet eredménye már nem tárolható a kijelölt u helyen. Ez az aritmetikai m veleteknél, pl. shifteléskor, szorzáskor, osztáskor fordulhat el . Az u o 1 érték átvitel mindig túlcsordulást jelez. u Egy el jeles bájt el jeles kiterjesztésén (sign extension) azt a m veletet értjük, mikor a o o u bájtot szó méret számmá alakítjuk át úgy, hogy mindkett ugyanazt az értéket képviselje. Ezt u o úgy végezzük el, hogy a cél szó fels bájtjának minden bitjét a kiinduló bájt el jelbitjével töltjük o o fel. Tehát pl. a -3d = 11111101b szám el jeles kiterjesztése az 11111111 11111101b szám lesz, o míg a +4d = 00000100b számból 00000000 00000100b lesz. Ezt a fogalmat általánosíthatjuk is: bájt kiterjesztése duplaszóvá, szó kiterjesztése duplaszóvá, kvadraszóvá stb. Az el z vel rokon fogalom az el jeltelen kiterjesztés vagy más néven zéró-kiterjesztés o o o (zero extension). Egy el jeltelen bájt el jeltelen kiterjesztésekor a bájtot olyan szóvá alakíto o juk át, amely a bájttal megegyez értéket tartalmaz. Ehhez a cél szó hiányzó, fels bájtjának o o minden bitjét 0-val kell feltölteni. Tehát pl. a 5d = 00000101b szám el jeltelen kiterjesztése o az 00000000 00000101b szám, míg a 128d = 10000000b számot 00000000 10000000b alakra hozzuk. Ez a fogalom is általánosítható és értelmezhet szavakra, duplaszavakra stb. is. o

4. fejezet

A 8086-os processzor jellemz i, o szolgáltatásai
Ismerkedjünk most meg az Intel 8086-os mikroprocesszorral közelebbr l. o

4.1. Memóriakezelés
A számítógép memóriáját úgy tudjuk használni, hogy minden egyes memóriarekeszt megszámozunk. Azt a módszert, ami meghatározza, hogy hogyan és mekkora területhez férhetünk hozzá egyszerre, memória-szervezésnek vagy memória-modellnek nevezzük. Több elterjedt modell létezik, közülük a legfontosabbak a lineáris és a szegmentált modellek. Lineáris modellen (linear memory model) azt értjük, ha a memória teljes területének valamely bájtja egyetlen számmal megcímezhet (kiválasztható). Ezt az értéket ekkor lineáris o memóriacímnek (linear memory address) nevezzük. Az elérhet (használható) lineáris címek o tartományát egy szóval címterületnek (address space) hívjuk. Szegmensen (segment) a memória egy összefügg , rögzített nagyságú darabját értjük most o (létezik egy kicsit másféle szegmens-fogalom is), ennek kezd címe (tehát a szegmens legels o o bájtjának memóriacíme) a szegmens báziscím avagy szegmenscím (segment base address). A szegmensen belüli bájtok elérésére szükség van azok szegmenscímhez képesti relatív távolságára, ez az offszetcím (offset address). Szegmentált modell (segmented memory model) esetén a logikai memóriacím (logical memory address) tehát két részb l tev dik össze: a szegmenso o címb l és az offszetcímb l. E kett érték már elegend a memória lefedéséhez. Jelölés: o o o o SZEGMENSCÍM:OFFSZETCÍM, tehát el ször leírjuk a szegmenscímet, azután egy kett spontot, majd az offszet jön. o o A 8086-os processzor 20 bites címbusszal rendelkezik, tehát a memóriacímek 20 bitesek lehetnek. Ez 1 Mbájt (= 220 = 1024 · 1024 bájt) méret memória megcímzéséhez elegend . A u o processzor csak a szegmentált címzést ismeri. A szegmensek méretét 64 Kbájtban szabták meg, mivel így az offszet 16 bites lesz, ez pedig belefér bármelyik regiszterbe (ezt majd kés bb látni o fogjuk). A szegmensek kezd címére is tettek azonban kikötést, mégpedig azt, hogy minden o szegmens csak 16-tal osztható memóriacímen kezd dhet (ezek a címek az ú.n. paragrafuso határok, a paragrafus pedig egy 16 bájt hosszú memóriaterület). Ez annyit tesz, hogy minden

4.2. REGISZTEREK

14

szegmenscím alsó 4 bitje 0 lesz, ezeket tehát felesleges lenne eltárolni. Így marad pont 16 bit a szegmenscímb l, azaz mind a szegmens-, mind az offszetcím 16 bites lett. Nézzünk egy példát: o 1234h:5678h A szegmenscím fels 16 bitje 1234h, ezért ezt balra shifteljük 4-szer, így kapjuk az 1 2340h-t. o Ehhez hozzá kell adni az 5678h számot. A végeredmény a 1 79B8h lineáris cím. Észrevehetjük, hogy ugyanazt a memóriarekeszt többféleképpen is megcímezhetjük, így pl. a 179Bh:0008h, 15AFh:1EC8h, 130Dh:48E8h címek mind az el z helyre hivatkoznak. Ezek közül van egy o o kitüntetett, a 179Bh:0008h. Ezt a címet úgy alkottuk meg, hogy a lineáris cím alsó négy bitje (1000b = 0008h) lesz az offszetcím, a fels 16 bit pedig a szegmenscímet alkotja. Ezért az o ilyen címeket nevezhetjük bizonyos szempontból ,,normáltnak" (normalized address), hiszen az offszet itt mindig 0000h és 000Fh között lesz. Most tekintsük a következ logikai címet: o 0FFFFh:0010h Az el bb leírtak szerint a szegmens bázicíme 0F FFF0h lesz, ehhez hozzá kell adni a 0 0010h o offszetcímet. A m veletet elvégezve az 10 0000h lineáris memóriacím lesz az eredmény. Aki u eléggé szemfüles, annak rögtön felt nhet valami: ez a cím 21 bit hosszú! Ez azért furcsa, u mert azt írtuk fenntebb, hogy a 8086-os processzor 20 bites memóriacímekkel tud dolgozni. Nos, ilyen esetekben a processzor ,,megszabadul" a legfels , 21. bitt l, tehát azt mindig 0-nak o o fogja tekinteni. (Matematikai szóhasználattal úgy fogalmazhatunk, hogy a processzor a lineáris címeket modulo 10 0000h képezi.) Így hiába 21 bites a lineáris cím, a memóriához ennek a címnek csak az alsó 20 bitje jut el. Ez azt eredményezi, hogy a 10 0000h és a 00 0000h lineáris címek ugyanarra a memóriarekeszre fognak hivatkozni. Ezt a jelenséget nevezik címterületkörbefordulásnak (address space wrap-around). Az elnevezés onnan ered, hogy a lineáris cím ,,megtörik", ,,átesik", ,,körbefordul" az 1 Mbájtos címterület fels határán. o Zárásként még egy fogalmat megemlítünk. Fizikai memóriacím (physical memory address) alatt azt a címet értjük, amit a memória a processzortól ,,megkap". Kicsit pontosítva, a címbuszra kerül értéket nevezzük fizikai címnek. A fizikai cím nem feltétlenül egyezik meg o a lineáris címmel. Erre éppen az el bb láthattunk példát, hiszen a 10 0000h lineáris címhez a o 00 0000h fizikai cím tartozik.

4.2. Regiszterek
Mint már a 2. fejezetben is említettük, a processzor (CPU) tartalmaz néhány speciális, közvetlenül elérhet tárolóhelyet, amiket regisztereknek nevezünk. (A ,,közvetlenül elérhet " o o jelz arra utal, hogy a regiszterek olvasásához/írásához nem kell a memóriához fordulnia a o processzornak). A 8086-os processzor összesen 14 db. 16 bites regiszterrel gazdálkodhat: · 4 általános célú adatregiszter (AX, BX, CX, DX) · 2 indexregiszter (SI és DI) · 3 mutatóregiszter (SP, BP, IP) · 1 státuszregiszter vagy Flags regiszter (SR vagy Flags)

4.2. REGISZTEREK

15

· 4 szegmensregiszter (CS, DS, ES, SS) Most nézzük részletesebben: · AX (Accumulator) ­ Sok aritmetikai utasítás használja a forrás és/vagy cél tárolására. · BX (Base) ­ Memóriacímzésnél bázisként szolgálhat. · CX (Counter) ­ Sok ismétléses utasítás használja számlálóként. · DX (Data) ­ I/O utasítások használják a portszám tárolására, ill. egyes aritmetikai utasítások számára is különös jelent séggel bír. o · SI (Source Index) ­ Sztringkezel utasítások használják a forrás sztring címének tároláo sára. · DI (Destination Index) ­ A cél sztring címét tartalmazza. · SP (Stack Pointer) ­ A verem tetejére mutat (azaz a verembe legutóbb berakott érték címét tartalmazza). · BP (Base Pointer) ­ Általános pointer, de alapesetben a verem egy elemét jelöli ki. · IP (Instruction Pointer) ­ A következ végrehajtandó utasítás memóriabeli címét tartalo mazza. Közvetlenül nem érhet el, de tartalma írható és olvasható is a vezérlésátadó o utasításokkal. · SR avagy Flags (Status Register) ­ A processzor aktuális állapotát, az el z m velet eredo o u ményét mutató, ill. a proci m ködését befolyásoló biteket, ú.n. flag-eket (jelz ket) tartalu o maz. Szintén nem érhet el közvetlenül, de manipulálható különféle utasításokkal. o · CS (Code Segment) ­ A végrehajtandó program kódját tartalmazó szegmens címe. Nem állítható be közvetlenül, csak a vezérlésátadó utasítások módosíthatják. · DS (Data Segment) ­ Az alapértelmezett, els dleges adatterület szegmensének címe. o · ES (Extra Segment) ­ Másodlagos adatszegmens címe. · SS (Stack Segment) ­ A verem szegmensének címe. A négy általános célú regiszter (AX, BX, CX és DX) alsó és fels nyolc bitje (azaz alsó és o fels bájtja) külön neveken érhet el: az alsó bájtokat az AL, BL, CL, DL, míg a fels bájtokat az o o o AH, BH, CH, DH regiszterek jelölik, amint ezt a 4.1. ábra is mutatja (a rövidítésekben L ­ Low, H ­ High). Éppen ezért a következ (Pascal-szer ) m veletek: o u u
AX:=1234h AL:=78h AH:=56h

végrehajtása után AX tartalma 5678h lesz, AH 56h-t, AL pedig 78h-t fog tartalmazni. A Flags regiszter felépítése a 4.2. ábrán látható. Az egyes bitek a következ információt o szolgáltatják:

4.2. REGISZTEREK

16

AX

15

8 7

0

AH

AL

4.1. ábra. Az AX regiszter szerkezete

OD I T S Z
15 8 7

A

P

C
0

4.2. ábra. A Flags regiszter szerkezete · C (Carry) ­ 1, ha volt aritmetikai átvitel az eredmény legfels bitjénél (el jeltelen aritmeo o tikai túlcsordulás), 0, ha nem. · P (Parity even) ­ 1, ha az eredmény legalsó bájtja páros számú 1-es bitet tartalmaz, különben 0. · A (Auxilliary carry, Adjust) ­ 1 jelzi, ha volt átvitel az eredmény 3-as és 4-es bitje (tehát az alsó és a fels nibble) között. o · Z (Zero) ­ Értéke 1, ha az eredmény zérus lett. · S (Sign) ­ Értéke az eredmény legfels bitjének, az el jelbitnek a tükörképe. o o · T (Trap) ­ Ha 1, akkor a lépésenkénti végrehajtás (single-step execution) engedélyezve van. · I (Interrupt enable) ­ Ha 1, akkor a maszkolható hardver-megszakítások engedélyezettek. · D (Direction) ­ Ha 0, akkor a sztringutasítások növelik SI-t és/vagy DI-t, különben csökkentés történik. · O (Overflow) ­ Ha 1, akkor el jeles aritmetikai túlcsordulás történt. o Ha hivatkozunk valamelyik flag-re, akkor a fenti bet jelekhez még hozzáírjuk az ,,F" bet t u u is. A CF, IF és DF flag-ek értékét közvetlenül is befolyásolhatjuk. Ezért pl. a CF nem csak akkor lehet 1, ha volt túlcsordulás (átvitel), hanem saját célból egyéb dolgot is jelezhet. A Flags regiszter 1, 3, 5, 12, 13, 14 és 15 számú bitjei fenntartottak. Ezek értéke gyárilag rögzített, így nem is módosíthatjuk oket.

4.3. ADATTÍPUSOK

17

Aritmetikai flag-ek alatt, ha külön nem mondjuk, a CF, PF, AF, ZF, SF és OF flag-eket értjük. Ha azt akarjuk kifejezni, hogy két vagy több regiszter tartalmát egymás után f zve akarunk u megadni egy értéket, akkor az egyes regisztereket egymástól kett sponttal elválasztva soroljuk o fel, szintén a helyiértékes felírást követve. Tehát pl. a DX:AX jelölés azt jelenti, hogy a 32 bites duplaszónak az alsó szava AX-ben, fels szava DX-ben van (a leírási sorrend megfelel a o bitek sorrendjének a bájtokban). Röviden: a DX:AX regiszterpár (register pair) egy 32 bites duplaszót tartalmaz. Ez a jelölés azonban bizonyos esetekben mást jelent, mint a memóriacímek ábrázolására használt SZEGMENS:OFFSZET felírások! Tehát a DX:AX jelölés jelenthet egy logikai memóriacímet DX szegmenssel és AX offszettel, de jelentheti azt is, hogy mi egy 32 bites értéket tárolunk a nevezett két regiszterben (ami azonban lehet egy lineáris cím is), és ennek alsó szavát AX, fels szavát pedig DX tartalmazza. o Ha általános (célú) regiszterekr l beszélünk, akkor általában nemcsak az AX, BX, CX és o DX regiszterekre gondolunk, hanem ezek 8 bites párjaira, továbbá az SI, DI, SP, BP regiszterekre is. A következ végrehajtásra kerül utasítás (logikai) címét a CS:IP, míg a verem tetejét az o o SS:SP regiszterpárok által meghatározott értékek jelölik. A CS és IP regisztereken kívül mindegyik regiszter tartalmazhat bármilyen értéket, tehát nem csak az eredeti funkciójának megfelel tartalommal tölthetjük fel oket. Ennek ellenére az o SS, SP és Flags regiszterek más célú használata nem javasolt.

4.3. Adattípusok
A szó méretét 2 bájtban állapították meg az Intel-nél, ezért pl. a BX és DI regiszterek szavasak, BL és DH bájtosak, míg az 1234 5678h szám egy duplaszó. A processzor csak egészekkel tud dolgozni, azon belül ismeri az el jeles és el jeltelen arito o metikát is. A több bájt hosszú adatokat little-endian módon tárolja, ezért pl. a fenti 1234 5678h szám a következ módon lesz eltárolva: a legalacsonyabb memóriacímre kerül a 78h bájt, ezt o követi az 56h, majd a 34h, végül pedig a 12h. Logikai, lebeg pontos valós típusokat nem támoo gat a processzor! Az egészek részhalmazát alkotják a binárisan kódolt decimális egész számok (Binary Coded Decimal numbers ­ BCD numbers). Ezek két csoportba sorolhatók: vannak pakolt (packed) és pakolatlan BCD számok (unpacked BCD number). (Használják még a ,,csomagolt" és ,,kicsomagolt" elnevezéseket is.) Pakolt esetben egy bájtban két decimális számjegyet tárolnak úgy, hogy a 4 ­ 7 bitek a szám magasabb helyiérték jegyét, míg a 0 ­ 3 bitek az alacsonyabb helyiu érték jegyet tartalmazzák. A számjegyeket a 0h ­ 9h értékek valamelyike jelöli. Pakolatlan u esetben a bájtnak a fels fele kihasználatlan, így csak 1 jegyet tudunk 1 bájtban tárolni. o A sztring (string) adattípus bájtok vagy szavak véges hosszú folytonos sorát jelenti. Ezzel a típussal b vebben egy kés bbi fejezetben foglalkozunk. o o Speciális egész típus a mutató (pointer). Mutatónak hívunk egy értéket, ha az egy memóriacímet tartalmaz, azaz ha azt a memória elérésénél felhasználjuk. Két típusa van: a közeli vagy rövid mutató (near, short pointer) egy offszetcímet jelent, míg távoli, hosszú vagy teljes mutatón (far, long, full pointer) egy teljes logikai memóriacímet, tehát szegmens:offszet alakú címet értünk. A közeli mutató hossza 16 bit, a távolié pedig 32 bit. Fontos, hogy mutatóként bármilyen értéket felhasználhatunk. Szintén jó, ha tudjuk, hogy a távoli mutatóknak az offszet része helyezkedik el az alacsonyabb memóriacímen a little-endian tárolásnak megfelel en, amit o a szegmens követ 2-vel magasabb címen.

4.4. MEMÓRIAHIVATKOZÁSOK, CÍMZÉSI MÓDOK

18

4.4. Memóriahivatkozások, címzési módok
Láttuk, hogy a memória szervezése szegmentált módon történik. Most lássuk, ténylegesen hogy adhatunk meg memóriahivatkozásokat. Azokat a, regiszterek és konstans számok (kifejezések) kombinációjából álló jelöléseket, amelyek az összes lehetséges szabályos memóriacímzési esetet reprezentálják, címzési módoknak (addressing mode) nevezzük. Több típusuk van, és mindenhol használható mindegyik, ahol valamilyen memóriaoperandust meg lehet adni. A memóriahivatkozás jelzésére a szögletes zárójeleket ([ és ]) használjuk.

4.4.1. Közvetlen címzés
A címzés alakja [offs16], ahol offs16 egy 16 bites abszolút, szegmensen belüli offszetet (rövid mutatót) jelöl.

4.4.2. Báziscímzés
A címzés egy bázisregisztert használ az offszet megadására. A lehetséges alakok: [BX],
[BP]. A cél bájt offszetcímét a használt bázisregiszterb l fogja venni a processzor. o

4.4.3. Indexcímzés
Hasonló a báziscímzéshez, m ködését tekintve is annak tökéletes párja. Alakjai: [SI], [DI]. u

4.4.4. Bázis+relatív címzés
Ide a [BX+rel8], [BX+rel16], [BP+rel8], [BP+rel16] formájú címmegadások tartoznak. A rel16 egy el jeles szó, rel8 pedig egy el jeles bájt, amit a processzor el jelesen kiterjeszt szóvá. o o o Ezek az ú.n. eltolások (displacement). Bármelyik változatnál a megcímzett bájt offszetjét a bázisregiszter tartalmának és az el jeles eltolásnak az összege adja meg. o

4.4.5. Index+relatív címzés
Hasonlóan a báziscímzés/indexcímzés pároshoz, ez a bázis+relatív címzési mód párja. Alakjai: [SI+rel8], [SI+rel16], [DI+rel8], [DI+rel16].

4.4.6. Bázis+index címzés
A két nevezett címzési mód keveréke. A következ formákat öltheti: [BX+SI], [BX+DI], o [BP+SI] és [BP+DI]. A regiszterek sorrendje természetesen közömbös, így [BP+SI] és [SI+BP] ugyanazt jelenti. A cél bájt offszetjét a két regiszter értékének összegeként kapjuk meg.

4.4.7. Bázis+index+relatív címzés
Ez adja a legkombináltabb címzési lehet ségeket. Általános alakjuk [bázis+index+rel8] és o [bázis+index+rel16] lehet, ahol bázis BX vagy BP, index SI vagy DI, rel8 és rel16 pedig el jeles o bájt ill. szó lehet.

4.4. MEMÓRIAHIVATKOZÁSOK, CÍMZÉSI MÓDOK

19

A bázis+relatív és index+relatív címzési módok másik elnevezése a bázisrelatív ill. indexrelatív címzés. A közvetlen címzésben szerepl abszolút offszetet, illetve a többi címzésnél el forduló elo o tolást nem csak számmal, de numerikus kifejezéssel is megadhatjuk. Megjegyezzük, hogy az említett címzési módoknak létezik egy alternatív alakja is. Ez annyiban tér el az el z kt l, hogy a többtagú címzések tagjait külön-külön szögletes zárójelekbe o o o írjuk, továbbá elhagyjuk a ,,+" jeleket. (Az eltolásban esetlegesen szerepl ,,-" jelre viszont o szükség van.) Az eltolást nem kötelez zárójelbe teni. A következ felsorolásban az egy sorban o o lev jelölések ekvivalensek: o · [BX+rel16], [BX][rel16], [BX]rel16 · [DI+rel8], [DI][rel8], rel8[DI] · [BP+SI], [BP][SI] · [BX+SI+rel16], [BX][SI][rel16], rel16[BX][SI] Minden címzési módhoz tartozik egy alapértelmezett (default) szegmensregiszter-el írás. o Ez a következ ket jelenti: o · bármely, BP-t nem tartalmazó címzési mód a DS-t fogja használni · a BP-t tartalmazó címzési módok SS-t használják Ha az alapértelmezett beállítás nem tetszik, akkor mi is el írhatjuk, hogy melyik szegmenso regisztert használja a cím meghatározásához a processzor. Ezt a szegmensfelülbíráló prefixekkel tehetjük meg. A prefixek alakja a következ : CS:, DS:, ES:, SS:, tehát a szegmensregiszter o neve plusz egy kett spont. Ezeket a prefixeket legjobb közvetlenül a címzési mód jelölése elé o írni. Most lássunk néhány példát szabályos címzésekre: · [12h*34h+56h] · ES:[09D3h] · CS:[SI-500d] · SS:[BX+DI+1999d] · DS:[BP+SI] A következ jelölések viszont hibásak: o · [AX] · [CX+1234h] · [123456789ABCh] · [SI+DI] · [SP+BP] · [IP] (az AX regiszter nem használható címzésre) (a CX regiszter sem használható címzésre) (túl nagy az érték) (két indexregiszter nem használható egyszerre) (az SP regiszter nem használható címzésre) (az IP regiszter nem érhet el) o (közvetlen címzés) (közvetlen címzés, szegmensfelülbírálással) (indexrelatív címzés, szegmensfelülbírálással) (bázis+index+relatív címzés, szegmensfelülbírálással) (bázis+index címzés, szegmensfelülbírálással)

4.5. VEREMKEZELÉS

20

A használt címzésmód által hivatkozott logikai memóriacímet (ill. gyakran csak annak offszet részét) tényleges vagy effektív címnek (effective address) nevezzük. Így pl. ha BX értéke 1000h, akkor a [BX+0500h] címzésmód effektív címe a DS:1500h, hiszen bázisrelatív címzéskor a bázisregiszter (BX) és az eltolás (0500h) értéke összeadódik egy offszetcímet alkotva, továbbá a korábbi megjegyzés alapján a szegmenscímet DS tartalmazza.

4.5. Veremkezelés
Vermen (stack) a következ tulajdonságokkal rendelkez adatszerkezetet értjük: o o · értelmezve van rajta egy Push m velet, ami egy értéket rak be a verem tetejére u · egy másik m velet, a Pop, a verem tetején lev adatot olvassa ki és törli a veremb l u o o · m ködése a LIFO (Last In First Out) elvet követi, azaz a legutoljára betett adatot vehetjük u ki el ször a veremb l o o · verem méretén azt a számot értjük, ami meghatározza, hogy maximálisan mennyi adatot képes eltárolni · verem magassága a benne lev adatok számát jelenti o · ha a verem magassága 0, akkor a verem üres · ha a verem magassága eléri a verem méretét, akkor a verem tele van · üres veremb l nem vehetünk ki adatot o · teli verembe nem rakhatunk be több adatot Az Intel 8086-os processzor esetén a verem mérete maximálisan 64 Kbájt lehet. Ez annak a következménye, hogy a verem csak egy szegmensnyi területet foglalhat el, a szegmensek mérete pedig szintén 64 Kbájt. A verem szegmensét az SS regiszter tárolja, míg a verem tetejére az SP mutat. Ez a verem lefelé b vül (expand-down) verem, ami azt jelenti, hogy az újonnan o o betett adatok egyre alacsonyabb memóriacímen helyezkednek el, és így SP értéke is folyamatosan csökken a Push m veletek hatására. SP-nek mindig páros értéket kell tartalmaznia, és a u verembe betenni ill. onnan kiolvasni szintén csak páros számú bájtot lehet. (Ez egy kis füllentés, de a legjobb, ha követjük ezt a tanácsot.) A lefelé b vülésb l következik, hogy SP aktuális értékéb l nem derül ki, hány elem is van o o o a veremben (azaz milyen magas a verem). Ugyanígy nem tudható, mekkora a verem mérete. Ehhez szükség van a verem alja kezd címének (azaz SP legnagyobb használható értékének) o ismeretére. Az az egy biztos, hogy egészen addig pakolhatunk elemet a verembe, amíg SP el nem éri a 0000h-t. A Push és Pop m veleteknek megfelel utasítások mnemonikja szintén PUSH ill. POP, így u o megjegyzésük nem túl nehéz. A PUSH el ször csökkenti SP-t a betenni kívánt adat méretének o megfelel számú bájttal, majd az SS:SP által mutatott memóriacímre beírja a kérdéses értéket. o A POP ezzel ellentétes m ködés , azaz el ször kiolvas az SS:SP címr l valahány számú bájtot, u u o o majd SP-hez hozzáadja a kiolvasott bájtok számát. Nézzünk egy példát a verem m ködésének szemléltetésére! Legyen AX tartalma 1212h, u BX tartalma 3434h, CX pedig legyen egyenl 5656h-val. Tekintsük a következ Assembly o o programrészletet:

4.5. VEREMKEZELÉS

21
PUSH PUSH POP PUSH POP POP AX BX AX CX BX CX

SP legyen 0100h, SS értéke most közömbös. A verem és a regiszterek állapotát mutatják a következ ábrák az egyes utasítások végrehajtása után: o

Kezdetben: SS:0102h : ?? SP SS:0100h : ?? SS:00FEh : ?? SS:00FCh : ?? SS:00FAh : ?? AX = 1212h, BX = 3434h, CX = 5656h
PUSH AX után: SS:0102h : ?? SS:0100h : ?? SP SS:00FEh : 1212h SS:00FCh : ?? SS:00FAh : ?? AX = 1212h, BX = 3434h, CX = 5656h PUSH BX után: SS:0102h : ?? SS:0100h : ?? SS:00FEh : 1212h SP SS:00FCh : 3434h SS:00FAh : ?? AX = 1212h, BX = 3434h, CX = 5656h POP AX után: SS:0102h : ?? SS:0100h : ?? SP SS:00FEh : 1212h SS:00FCh : 3434h SS:00FAh : ?? AX = 3434h, BX = 3434h, CX = 5656h PUSH CX után: SS:0102h : ?? SS:0100h : ?? SS:00FEh : 1212h SP SS:00FCh : 5656h SS:00FAh : ?? AX = 3434h, BX = 3434h, CX = 5656h

4.5. VEREMKEZELÉS

22
POP BX után: SS:0102h : ?? SS:0100h : ?? SP SS:00FEh : 1212h SS:00FCh : 5656h SS:00FAh : ?? AX = 3434h, BX = 5656h, CX = 5656h

POP CX után: SS:0102h : ?? SP SS:0100h : ?? SS:00FEh : 1212h SS:00FCh : 5656h SS:00FAh : ?? AX = 3434h, BX = 5656h, CX = 1212h

Az utasítások végrehajtása tehát azt eredményezi, hogy AX felveszi BX értékét, BX a CX értékét, CX pedig az AX eredeti értékét (olyan, mintha ,,körbeforgattuk" volna a regiszterek tartalmát egymás között). Mivel ugyanannyi PUSH volt, mint amennyi POP, SP értéke a végrehajtás után visszaállt a kezdetire. Figyeljük meg, hogy a PUSH-sal berakott értékek a memóriában bentmaradnak akkor is, ha kés bb egy POP-pal kivesszük oket, bár ekkor már nem részei a veremnek (azaz nincsenek a o veremben). Ez nem túl meglep dolog, ha figyelembe vesszük az utasítások m ködését. o u A verem sok dologra használható a programozás során: · regiszterek, változók értékének megcserélésére · regiszterek ideiglenes tárolására · lokális változók elhelyezésére · eljáráshíváskor az argumentumok átadására, ill. a visszatérési érték tárolására A veremregisztereknek (SS és SP) mindig érvényes értékeket kell tartalmazniuk a program futásakor, mivel azt a processzor és más programok (pl. a hardvermegszakításokat lekezel o rutinok) is használják.

4.6. I/O, MEGSZAKÍTÁS-RENDSZER

23

4.6. I/O, megszakítás-rendszer
Ez a processzor 16 biten ábrázolja a portszámokat, így összesen 65536 db. portot érhetünk el a különféle I/O (Input/Output ­ ki-/bemeneti) utasításokkal. Ezek közül a 0000h és 00FFh közötti tartományt az alaplapi eszközök használják, és mivel a legfontosabb perifériák címét rögzítették, a különböz PC-s rendszerekben ezeket ugyanazon a porton érhetjük el. Bármeo lyik porton kezdeményezhetünk olvasást és írást egyaránt, de ezzel azért nem árt vigyázni, egy félresikeredett írással ugyanis könnyen ,,megadásra" késztethetjük a számítógépet. Egy megszakítást kiválthat egy hardvereszköz (ez a hardver-megszakítás), de a felhasználó és a programok is elindíthatnak ilyen eseményt (ez a szoftver-megszakítás). A megszakítások két típusa nem csak a számukban, de tulajdonságaikban is eltér egymástól. A hardver-megszakításokat a megszakítás-vezérl n keresztül érzékeli a processzor. A veo zérl 8 különböz megszakítás-vonalat tart fent (AT-k esetén két vezérl t kötöttek sorba, így o o o lett a 8-ból 16 vonal). Ezen megszakításokat az IRQ0, IRQ1, . . . , IRQ7 (továbbá IRQ8, . . . , IRQ15) szimbólumokkal jelöljük. Fontos jellemz jük, hogy a program futásával párhuzamosan o aszinkron keletkeznek, és hogy maszkolhatók. Ez utóbbi fogalom azt takarja, hogy az egyes IRQ vonalakat egyenként engedélyezhetjük ill. tilthatjuk le. Ha egy vonal tiltva (másképpen maszkolva) van, akkor az arra vonatkozó kérések nem jutnak el a processzorhoz. Ezen kívül a processzor Flags regiszterének IF bitjével az összes hardver-megszakítás érzékelését letilthatjuk ill. újra engedélyezhetjük. Ezzel szemben szoftver-megszakításból 256 darab van, jelölésükre az INT 00h, . . . , INT 0FFh kifejezéseket használjuk. A program futása során szinkron keletkeznek (hiszen egy gépi kódú utasítás váltja ki oket), és nem maszkolhatók, nem tilthatók le. Az IF flag állásától függetlenül mindig érzékeli oket a CPU. A két megszakítás-típus lekezelésében közös, hogy a CPU egy speciális rutint (programrészt) hajt végre a megszakítás detektálása után. Ez a rutin a megszakítás-kezel (interrupt o handler). Az operációs rendszer felállása után sok hardver- és szoftver-megszakításnak már van kezel je (pl. a BIOS is jópár ilyen megszakítást üzemeltet), de lehet ség van arra is, hogy o o bármelyik ilyen rutint lecseréljük egy saját programunkra. A hardver-megszakítások a szoftveresek egy adott tartományára képz dnek le, ez alapállao potban az INT 08h, . . . , INT 0Fh (IRQ8-tól IRQ15-ig pedig az INT 70h, . . . , INT 77h) megszakításokat jelenti, tehát ezek a megszakítások másra nem használhatók. Így pl. IRQ4 keletkezésekor a processzor az INT 0Ch kezel jét hívja meg, ha IF = 1 és az IRQ4 nincs maszkolva. o A megszakítások egy speciális csoportját alkotják azok, amiket valamilyen vészhelyzet, súlyos programhiba vált ki. Ezeket összefoglaló néven kivételnek (exception) nevezzük. Kivételt okoz pl. ha nullával próbálunk osztani, vagy ha a verem túl- vagy alulcsordul.

5. fejezet

Az Assembly nyelv szerkezete, szintaxisa
Az Assembly nyelven megírt programunkat egy szöveges forrásfájlban tároljuk, amit aztán egy erre specializált fordítóprogrammal átalakítunk tárgykóddá (object code). A tárgykód még nem futtatható közvetlenül, ezért a szerkeszt (linker) elvégzi rajta az átalakításokat. Végül o az egy vagy több tárgykód összef zése után kialakul a futtatható program. Az említett fordítóu programot assemblernek nevezzük. A forrásfájlok állománynév-kiterjesztése általában .ASM vagy .INC, a tárgykódé .OBJ, a futtatható fájloké pedig .COM vagy .EXE. Ezek az elnevezések a PC-s DOS rendszerekre vonatkoznak, más operációs rendszerek alatt (pl. UNIX, OS/2, Linux) ezek eltérhetnek. Az Assembly forrásfájloknak tiszta szöveges (text) állományoknak kell lennie, mert az assemblerek nem szeretik a mindenféle ,,bináris szemetet" tartalmazó dokumentumokat. Az Assembly nem érzékeny a kis- és nagybet kre, így tehát a PUSH, Push, push, PusH u szavak ugyanazt a jelentést képviselik. Ez elég nagy szabadságot ad a különböz azonosítók o elnevezésére, és a forrást is áttekinthet bbé teszi. o Az Assembly forrás sorai a következ elemeket tartalmazhatják: o · assembler utasítások (f leg ezek alkotják a tényleges program kódját) o · pszeudo utasítások (pl. makrók, helyettesít szimbólumok) o · assembler direktívák A direktívák (directive) olyan, az assemblernek szóló utasítások, melyek közvetlenül gépi kódot nem feltétlenül generálnak, viszont a fordítás menetét, az elkészült kódot befolyásolják. Az érvényes assembler utasítások szintaxisa a következ : o
{Címke:} {Utasítás {Operandus<,Operandus>}} {;Megjegyzés}

A kapcsos zárójelek ({ és }) az opcionális (nem kötelez ) részeket jelzik, míg a csúcsos zárójeo lek (< és >) 0 vagy több el fordulásra utalnak. Ezek alapján az assembler utasítások szintaxisa o szavakban a következ : a sor elején állhat egy Címke, amit követhet valahány Prefix, majd o írhatunk legfeljebb egy Utasítást, aminek valahány Operandusa lehet, s végül a sort egy Megjegyzés zárhatja. A megfogalmazás mutatja, hogy a csak egy címkét, megjegyzést tartalmazó sorok, s t még az üres sor is megengedett az Assembly forrásban. o

25 A címke (label) deklarációja az azonosítójából és az azt követ kett spontból áll. Értéke o o az aktuális sor memóriacíme (helyesebben offszetje). Szerepe azért fontos, mert segítségével könnyen írhatjuk le a különféle vezérlési szerkezeteket. A gépi kódú utasítások ugyanis relatív vagy abszolút memóriacímekkel dolgoznak, nekünk viszont kényelmesebb, ha egy szimbólummal (a címke nevével) hivatkozunk a kérdéses sorra. A tényleges cím megállapítása a szerkeszt o feladata lesz. A prefix olyan utasításelem, amely csak az ot követ gépi kódú utasításra (avagy Assembly o mnemonikra) hat, annak m ködését változtatja meg ill. egészíti ki. u Ezeket követi az utasítás (instruction), ami egy assembler direktíva, egy pszeudo utasítás vagy egy Assembly mnemonik lehet. Emlékeztet ül, a mnemonik (mnemonic) azonos m veo u letet végz különböz gépi kódú utasítások csoportját jelöl szimbólum (szó). Az Intel mneo o o monikok legalább kétbet sek, és némelyik még számot is tartalmaz. A mnemonikok gépi kódú u alakját muveleti kódnak (operation code, opcode) nevezzük. A továbbiakban a ,,mnemonik" szó helyett inkább az ,,utasítást" fogjuk használni. Az mnemonik által meghatározott utasítás valamilyen ,,dolgokon" hajtja végre a feladatát. Ezeket a dolgokat mutatják az operandusok (operand), amelyeket (ha több van) vessz vel elo választva sorolunk fel. Operandusok épít kövei a következ k lehetnek: o o · regiszterek · numerikus (szám) konstansok · karakteres (sztring) konstansok · szimbólumok · operátorok (m veletek) u Regiszterekre a következ szavakkal utalhatunk: AL, AH, AX, BL, BH, BX, CL, CH, CX, o
DL, DH, DX, SI, DI, SP, BP, CS, DS, ES és SS.

A numerikus konstansnak számjeggyel kell kezd dnie, azután tartalmazhatja a tíz számo jegyet ill. az A ­ F bet ket, a végén pedig a számrendszerre utaló jelölés állhat. Alapesetben u minden számot decimálisnak értelmez az assembler, kivéve ha: · átállítottuk az alapértelmezést a RADIX direktívával · a szám végén a ,,d" (decimális), ,,b" (bináris), ,,h" (hexadecimális) vagy ,,o" (oktális) bet u áll Fontos, hogy a bet vel kezd d hexadecimális számok elé tegyünk legalább egy 0-t, mert küu o o lönben szimbólumnak próbálná értelmezni a számunkat az assembler. Karakteres konstansokat aposztrófok('. . . ') vagy idéz jelek (". . . ") között adhatunk meg. o Ha numerikus konstans helyett állnak, akkor lehetséges hosszuk az adott direktívától (pl. a DW esetén 2 bájt) ill. a másik operandustól függ. Szimbólumok például a konstans azonosítók, változók, címkék, szegmensek, eljárások nevei. Szimbólum azonosítója tartalmazhat számjegyet, bet ket (csak az angol ábécé bet it), aláu u húzásjelet ( _ ), dollárjelet ($) és ,,kukacot" (@). Fontos, hogy az azonosítók nem kezd dhetnek o számjeggyel! Speciális szimbólum az egymagában álló dollárjel, ennek neve pozíció-számláló (location counter). Értéke az aktuális sor szegmensbeli (avagy szegmenscsoportbeli) offszetje. Operátorból rengeteg van, a legfontosabbak talán a következ k: o

26 · kerek zárójelek · szokásos aritmetikai m veleti jelek (+, -, *, /, MOD) u · mez kiválasztó operátor (.) o · szegmens-el írás/-felülbírálás (:) o · memóriahivatkozás ([. . . ]) · bitenkénti logikai m veletek (AND, OR, XOR, NOT) u · típusfelülbírálás (PTR) · adattípus megadás (BYTE, WORD, DWORD) · duplikáló operátor (DUP) · relációs operátorok (EQ, NE, GT, GE, LT, LE) · pointer típusok (NEAR, FAR) · szegmens/offszet lekérdezés (SEG, OFFSET) · léptet operátorok (SHL, SHR) o · méretlekérdez operátorok (LENGTH, SIZE, TYPE, WIDTH) o Az operandusok ezek alapján három csoportba sorolhatók: regiszter-, memória- és konstans (közvetlen érték ) operandusok. A különböz mnemonikok lehetnek nulla-, egy-, kett -, u o o három- vagy négyoperandusúak. Fontos megjegyezni, hogy a kétoperandusú mnemonikok els operandusa a cél, a második o pedig a forrás. Ezt a konvenciót egyébként csak az Intel Assembly használja, az összes többi (pl. Motorola) Assemblyben az els a forrás, a második pedig a céloperandus. Az elnevezések o arra utalnak, hogy a m velet eredménye a céloperandusban tárolódik el. Ha szükséges, a céu loperandus második forrásként is szolgálhat. Egy operandust használó utasításoknál az az egy operandus sokszor egyben forrás és cél is. Végül az assembler utasítás sorának végén megjegyzést is írhatunk egy pontosvessz után. o Ezeket a karaktereket az assembler egyszer en átugorja, így tehát bármilyen szöveget tartalu mazhat (még ékezetes bet ket is). A megjegyzés a pontosvessz t l a sor végéig tart. u oo Most tisztáznunk kell még néhány fogalmat. Korábban már említettük, hogy a processzor szegmentált memória-modellt használ. Hogy teljes legyen a kavarodás, az assemblernek is el o kell írnunk a memória-modellt, és szegmenseket is létre kell hoznunk. A két szegmens- és memória-modell fogalmak azonban egy picit különböznek. Az assemblerben létrehozott szegmenseknek van nevük (azonosítójuk, ami tehát egy szimbólum), méretük legfeljebb 64 Kbájt lehet, és nem feltétlenül kell összefügg nek lennie egy o szegmens definíciójának. Ez utóbbi tulajdonság több szabadságot enged a programozónak, mivel megteheti, hogy elkezd egy szegmenst, majd definiál egy másikat, azután megint visszatér az els szegmensbe. Az azonos néven megkezdett szegmensdefiníciókat az assembler és a szero keszt szépen összef zi egyetlen darabbá, tehát ez a jelölésrendszer a processzor számára átláto u szó lesz. Másrészt a méretre vonatkozó kijelentés mindössze annyit tesz, hogy ha nem töltünk ki teljesen (kóddal vagy adatokkal) egy memóriaszegmenst (ami viszont 64 Kbájt nagyságú),

27 akkor a fennmaradó bájtokkal az assembler nem foglalkozik, a program futásakor ezek valami ,,szemetet" fognak tartalmazni. A memória-modell el írása akkor fontos, ha nem akarunk foglalkozni különböz szegmeno o sek (adat-, kód-, verem- stb.) létrehozásával, és ezt a folyamatot az assemblerre akarjuk hagyni. Ehhez viszont jeleznünk kell az assemblernek, hogy el reláthatólag mekkora lesz a programunk o memóriaigénye, hány és milyen típusú szegmensekre van szükségünk. Ezt a típusú szegmensmegadást egyszerusített szegmensdefiníciónak (simplified segment definition) nevezzük. A két módszert keverve is használhatjuk kedvünk és igényeink szerint, nem fogják egymást zavarni (mi is ezt fogjuk tenni). Ha magas szint nyelvhez írunk küls Assembly rutinokat, u o akkor az egyszer sített szegmensmegadás sokszor egyszer bb megoldást szolgáltat. u u Most pedig lássunk néhány fontos direktívát: · modul/forrásfájl lezárása (END) · szegmens definiálása (SEGMENT . . . ENDS) · szegmenscsoport definiálása (GROUP) · szegmens hozzárendelése egy szegmensregiszterhez (ASSUME) · értékadás a $ szimbólumnak (ORG) · memória-modell megadása (MODEL) · egyszer sített szegmensdefiníciók (CODESEG, DATASEG, FARDATA, UDATASEG, UFARu DATA, CONST, STACK, .CODE, .DATA, .STACK) · helyfoglalás (változó létrehozása) (DB, DW, DD, DF, DQ, DT) · konstans/helyettesít szimbólum létrehozása (=, EQU) o · címke létrehozása (LABEL) · eljárás definiálása (PROC . . . ENDP) · küls szimbólum definiálása (EXTRN) o · szimbólum láthatóvá tétele a külvilág számára (PUBLIC) · feltételes fordítás el írása (IF, IFccc, ELSE, ELSEIF, ENDIF) o · küls forrásfájl beszúrása az aktuális pozícióba (INCLUDE) o · felhasználói típus definiálása (TYPEDEF) · struktúra, unió, rekord definiálása (STRUC . . . ENDS, UNION . . . ENDS, RECORD) · a számrendszer alapjának átállítása (RADIX) · makródefiníció (MACRO . . . ENDM) · makróm veletek (EXITM, IRP, IRPC, PURGE, REPT, WHILE) u · utasításkészlet meghatározása (P8086, P186, P286, P386 stb.; .8086, .186, .286, .386 stb.)

28 A direktívák használatára kés bb még visszatérünk. o Assemblerb l több is létezik PC-re, ilyenek pl. a Microsoft Macro Assembler (MASM), o Turbo Assembler (TASM), Netwide Assembler (NASM), Optimizing Assembler (OPTASM). Ebben a jegyzetben a programokat a TASM jelölésmódban írjuk le, de több-kevesebb átírással másik assembler alá is átvihet k a források. A TASM specialitásait nem fogjuk kihasználni, o ahol nem muszáj. A TASM saját linkert használ, ennek neve Turbo Linker (TLINK).

6. fejezet

A 8086-os processzor utasításkészlete
Itt az ideje, hogy megismerkedjünk a kiválasztott processzorunk által ismert utasításokkal (pontosabban mnemonikokkal). Az utasítások közös tulajdonsága, hogy az operandusoknak teljesíteniük kell a következ feltételeket: o · Ha egyetlen operandusunk van, akkor az általában csak az általános regiszterek közül kerülhet ki, vagy pedig memóriahivatkozás lehet (néhány esetben azonban lehet konstans kifejezés vagy szegmensregiszter is). · Két operandus esetén bonyolultabb a helyzet: mindkét operandus egyszerre nem lehet szegmensregiszter, memóriahivatkozás és közvetlen (konstans) érték, továbbá szegmensregiszter mellett vagy általános regiszternek vagy memóriahivatkozásnak kell szerepelnie. (A sztringutasítások kivételek, ott mindkét operandus memóriahivatkozás lesz, de err l majd kés bb.) A két operandus méretének majdnem mindig meg kell egyeznie, ez o o alól csak néhány speciális eset kivétel.

6.1. Prefixek
El ször lássuk a prefixeket, zárójelben megadva a gépi kódjukat is (ez utóbbit persze nem o kell megjegyezni):

6.1.1. Szegmensfelülbíráló prefixek
Ezek a CS: (2Eh), DS: (3Eh), ES: (26h) és SS: (36h). A TASM ezeken kívül ismeri a SEGCS, SEGDS, SEGES és SEGSS mnemonikokat is. A SCAS és a STOS utasítások ki vételével bármely más utasítás el tt megadva oket az alapértelmezés szerinti szegmensregiszter o helyett használandó regisztert adják meg. Természetesen ez csak a valamilyen memóriaoperandust használó utasításokra vonatkozik.

6.2. UTASÍTÁSOK

30

6.1.2. Buszlezáró prefix
Mnemonikja LOCK (0F0h). Hatására az adott utasítás által módosított memóriarekeszt az utasítás végrehajtásának idejére a processzor zárolja úgy, hogy a buszokhoz más hardverelem nem férhet hozzá a m velet lefolyásáig. (A teljesség kedvéért: ebben a processzor LOCK# u jel kivezetése vesz részt.) Csak a következ utasítások esetén használható: ADD, ADC, SUB, u o SBB, AND, OR, XOR, NOT, NEG, INC, DEC, XCHG, más esetekben hatása definiálatlan. Az említett utasítás céloperandusának kötelez en memóriahivatkozásnak kell lennie. o

6.1.3. Sztringutasítást ismétl prefixek o
Lehetséges alakjaik: REP, REPE, REPZ (0F3h), REPNE, REPNZ (0F2h). Csak a sztringkezel utasítások el tt használhatók, más esetben következményük kiszámíthatatlan. M ködéo o u sükr l és használatukról kés bb szólunk. o o

6.2. Utasítások
Most jöjjenek az utasítások, típus szerint csoportosítva:

6.2.1. Adatmozgató utasítások
· MOV ­ adatok mozgatása · XCHG ­ adatok cseréje · PUSH ­ adat betétele a verembe · PUSHF ­ Flags regiszter betétele a verembe · POP ­ adat kivétele a veremb l o · POPF ­ Flags regiszter kivétele a veremb l o · IN ­ adat olvasása portról · OUT ­ adat kiírása portra · LEA ­ tényleges memóriacím betöltése · LDS, LES ­ teljes pointer betöltése szegmensregiszter:általános regiszter regiszterpárba · CBW ­ AL el jeles kiterjesztése AX-be o · CWD ­ AX el jeles kiterjesztése DX:AX-be o · XLAT, XLATB ­ AL lefordítása a DS:BX cím fordító táblázattal u · LAHF ­ Flags alsó bájtjának betöltése AH-ba · SAHF ­ AH betöltése Flags alsó bájtjába · CLC ­ CF flag törlése · CMC ­ CF flag komplementálása (invertálása)

6.2. UTASÍTÁSOK

31

· STC ­ CF flag beállítása · CLD ­ DF flag törlése · STD ­ DF flag beállítása · CLI ­ IF flag törlése · STI ­ IF flag beállítása

6.2.2. Egész számos aritmetika
· ADD ­ összeadás · ADC ­ összeadás átvitellel (CF) együtt · SUB ­ kivonás · SBB ­ kivonás átvitellel (CF) együtt · CMP ­ összehasonlítás (flag-ek beállítása a cél és a forrás különbségének megfelel en) o · INC ­ inkrementálás (növelés 1-gyel), CF nem változik · DEC ­ dekrementálás (csökkentés 1-gyel), CF nem változik · NEG ­ kettes komplemens képzés (szorzás -1-gyel) · MUL ­ el jeltelen szorzás o · IMUL ­ el jeles szorzás o · DIV ­ el jeltelen maradékos osztás o · IDIV ­ el jeles maradékos osztás o

6.2.3. Bitenkénti logikai utasítások (Boole-muveletek)
· AND ­ logikai AND · OR ­ logikai OR · XOR ­ logikai XOR · NOT ­ logikai NOT (egyes komplemens képzés) · TEST ­ logikai bit tesztelés (flag-ek beállítása a két op. logikai AND-jének megfelel en) o

6.2. UTASÍTÁSOK

32

6.2.4. Bitléptet utasítások o
· SHL ­ el jeltelen léptetés (shiftelés) balra o · SAL ­ el jeles léptetés balra (ugyanaz, mint SHL) o · SHR ­ el jeltelen léptetés jobbra o · SAR ­ el jeles (aritmetikai) léptetés jobbra o · ROL ­ balra forgatás (rotálás) · RCL ­ balra forgatás CF-en át · ROR ­ jobbra forgatás · RCR ­ jobbra forgatás CF-en át

6.2.5. Sztringkezel utasítások o
· MOVS, MOVSB, MOVSW ­ sztring mozgatása · CMPS, CMPSB, CMPSW ­ sztringek összehasonlítása · SCAS, SCASB, SCASW ­ keresés sztringben · LODS, LODSB, LODSW ­ betöltés sztringb l o · STOS, STOSB, STOSW ­ tárolás sztringbe

6.2.6. Binárisan kódolt decimális (BCD) aritmetika
· AAA ­ ASCII igazítás összeadás után · AAS ­ ASCII igazítás kivonás után · AAM ­ ASCII igazítás szorzás után · AAD ­ ASCII igazítás osztás el tt o · DAA ­ BCD rendezés összeadás után · DAS ­ BCD rendezés kivonás után

6.2.7. Vezérlésátadó utasítások
· JMP ­ feltétel nélküli ugrás · JCXZ ­ ugrás, ha CX = 0000h · Jccc ­ feltételes ugrás (,,ccc" egy feltételt ír le) · LOOP ­ CX dekrementálása és ugrás, ha CX 0000h 0000h

· LOOPE, LOOPZ ­ CX dekrementálása és ugrás, ha ZF = 1 és CX

6.2. UTASÍTÁSOK

33 0000h

· LOOPNE, LOOPNZ ­ CX dekrementálása és ugrás, ha ZF = 0 és CX · CALL ­ eljárás (szubrutin) hívása · RET, RETF ­ visszatérés szubrutinból · INT ­ szoftver-megszakítás kérése · INTO ­ INT 04h hívása, ha OF = 1, különben NOP-nak felel meg · IRET ­ visszatérés megszakításból

6.2.8. Rendszervezérl utasítások o
· HLT ­ processzor leállítása, amíg megszakítás (vagy reset) nem érkezik

6.2.9. Koprocesszor-vezérl utasítások o
· WAIT ­ adatszinkronizálás a koprocesszorral · ESC ­ utasítás küldése a koprocesszornak

6.2.10. Speciális utasítások
· NOP ­ üres utasítás (igazából XCHG AX,AX) Az adatmozgató utasítások a leggyakrabban használt utasítások kategóriáját alkotják. Ez természetes, hiszen más (magasabb szint ) programozási nyelvekben is sokszor el fordul, hogy u o valamilyen adatot olvasunk ki vagy töltünk be egy változóba, avagy valamelyik portra. Kicsit kilógnak ebb l a sorból a CBW, CWD, XLAT utasítások, de mondjuk a flag-eket állító CLC, o CMC stb. utasítások is kerülhettek volna másik kategóriába. Az egész aritmetikás utasításokat szintén nagyon sokszor használjuk programozáskor. A CMP utasításnak pl. kulcsszerepe lesz a különféle elágazások (feltételes vezérlési szerkezetek) lekódolásában. A Boole-m veletekkel mindenféle bitm veletet (beállítás, maszkolás, tesztelés) el tudunk u u végezni, a munkák során éppen ezért nélkülözhetetlenek. Shiftel m veleteket f leg a már említett, 2 hatványával való szorzás gyors megvalósítására o u o alkalmazunk. Ezenkívül nagy precizitású, saját aritmetikai m veletek fejlesztésekor is fontos u szerepet kapnak a rotáló utasításokkal együtt. Sztringen (string) itt bájtok vagy szavak véges hosszú folytonos sorát értjük. A sztringeken operáló utasítások pl. a nagy mennyiség adatok másolásánál, vizsgálatánál segítenek. u BCD aritmetikát csak az emberi kényelem miatt támogat a processzor. Ezek az utasítások két csoportra oszthatók: pakolatlan (AAA, AAD, AAM, AAS) és pakolt (DAA,DAS) BCD aritmetikára. Kifejezetten ritkán használjuk oket, bár ez elég szubjektív kijelentés. A vezérlésátadó utasítások a programok egyik alappillérét alkotják, helyes használatuk a jól m köd algoritmus alapfeltétele. u o Rendszer- és koprocesszor-vezérl utasításokat csak speciális esetekben, küls hardverrel o o való kapcsolatfenntartásra és kommunikációra szokás használni. Különleges ,,csemege" a NOP (No OPeration), ami eredete szerint adatmozgató utasítás lenne (mivel az XCHG AX,AX utasítás álneve), viszont m ködését tekintve gyakorlatilag nem u

6.2. UTASÍTÁSOK

34

csinál semmit. Hasznos lehet, ha a kódba olyan üres bájtokat akarunk beszúrni, amit kés bb o esetleg valami egyéb célra használunk majd, de nem akarjuk, hogy a processzor azt valamilyen egyéb utasításnak értelmezve nemkívánatos mellékhatás lépjen fel. A NOP kódja egyetlen bájt (90h). Szegmensregiszter-operandust (ami nem egyenl a szegmensfelülbíráló prefixszel) a fenti o utasítások közül csak a MOV, PUSH és POP kaphat, de ezzel egyel re ne foglalkozzunk, kéo s bb még visszatérünk rájuk. o

7. fejezet

Assembly programok készítése
Az Assembly nyelv programozás folyamata 3 jól elkülöníthet fázisra bontható: forrás u o elkészítése, fordítás, majd szerkesztés. Nézzük meg egyenként ezeket a lépéseket! A programok forrásállományának a könnyebb azonosítás végett (és a hagyományok betartása érdekében is) célszer egy .ASM kiterjesztés nevet adni. A forrást bármilyen szöu u vegszerkeszt vel (pl. a DOS-os EDIT, valamilyen commander program szerkeszt je, Windows o o NotePad-je stb.) elkészíthetjük, csak az a fontos, hogy a kimentett állomány ne tartalmazzon mindenféle formázási információt, képeket stb., pusztán a forrás szövegét. Ha ez megvan, akkor jöhet a fordítás. A TASM program szintaxisa a következ : o TASM {opciók} forrás{,tárgykód}{,lista}{,xref} Az opciók különféle kapcsolók és paraméterek, amik a fordítás menetét és a generált fájlok tartalmát befolyásolják. A kimeneti állomány tárgykód (object, .OBJ kiterjesztéssel) formátumú, és alapesetben a fájl neve megegyezik a forrás nevével. Ha akarjuk, ezt megváltoztathatjuk. Kérhetjük az assemblert, hogy a fordítás végeztével készítsen listát és/vagy keresztreferenciatáblázatot (cross reference table, xref). A listában a forrás minden egyes sorához generált gépi kód fel van tüntetve, ezenkívül tartalmazza a definiált szegmensek adatai, valamint a használt szimbólumokat összegy jt szimbólumtáblát. A keresztreferencia-táblázat azt tartalmazza, u o hogy a különböz szimbólumokat hol definiálták, ill. mely helyeken hivatkoztak rájuk. Ez a fájl o bináris (tehát nem szöveges), értelmes információt bel le a TCREF programmal nyerhetünk. o Legtöbbször csak a lefordítandó forrás nevét adjuk meg a TASM-nak, hacsak nincs valamilyen különleges igényünk. Néhány fontos kapcsolót azért felsorolunk: · /a, /s ­ A szegmensek szerkesztési sorrendjét befolyásolják. Az els ábécé rend szerint, o míg a második a definiálás sorrendje szerinti szegmens-sorrendet ír el . Alapértelmezés o a /s. · /l, /la ­ A listázás formátumát befolyásolják: normál (opcio/l) ill. kib vített (/la) lista fog o készülni. Alapértelmezésben nem készül lista. · /m# ­ A fordítás # menetes lesz (a ,,#" helyére egy számot kell írni). Alapérték a 2. · /z ­ Hiba esetén a forrás megfelel sorát is kiírja. o · /zi, /zd, /zn ­ A tárgykódba bekerül nyomkövetési információt befolyásolják: teljes (/zi), o csak a programsorok címei (/zd) vagy semmi (/zn). Alapértelmezés a /zn.

36 Miután mindegyik forrást lefordítottuk tárgykódra, jöhet a szerkesztés. A TLINK is számos argumentummal indítható: TLINK {opciók} tárgykód fájlok{, futtatható fájl}{, térkép}{, könyvtárak} Az opciók esetleges megadása után kell felsorolni azoknak a tárgykódú állományoknak a neveit, amiket egy futtatható fájlba akarunk összeszerkeszteni. A f program tárgykódját kell legels o o nek megadnunk. A futtatható fájl nevét kívánságunk szerint megadhatjuk, különben a legels o tárgykódú fájl (a f program) nevét fogja kapni. A térképfájl (map file) az egyes modulokban o definiált szegmensek, szimbólumok adatait tartalmazza, valamint külön kérésre azoknak a programsoroknak a sorszámait, amikhez kód került lefordításra. A könyvtárakban (library) több tárgykódú állomány lehet összegy jtve, a program írásakor tehát ezekb l is használhatunk rutiu o nokat, ha akarunk. Most lássunk néhány megadható opciót: · /x, /m, /l, /s ­ A térkép (map) tartalmát befolyásolják: nincs térkép (/x), publikus szimbólumok listája lesz (/m), kódhoz tartozó sorszámok lesznek (/l), szegmensinformáció lesz (/s). Alapértelmezés a /s. · /c ­ Érzékeny lesz a kisbet kre és nagybet kre. u u · /v ­ Engedélyezi a nyomkövetési információk beépítését a futtatható fájlba. · /t ­ Az alapértelmezett .EXE helyett .COM típusú futtatható fájlt készít. Most lássunk egy példát! Tegyük fel, hogy elkészítettünk egy Assembly forrásfájlt PELDA.ASM néven, és hogy a programunk csak ebb l az állományból áll. A fordítást ekkor a o TASM PELDA utasítás kiadásával intézhetjük el. A fordítás során valami ilyesmi jelenik meg a képerny n: o
Turbo Assembler Assembling file: Error messages: Warning messages: Passes: Remaining memory: Version 4.0 pelda.asm None None 1 355k Copyright (c) 1988, 1993 Borland International

Az ,,Error messages:" címkéj sor a hibák, a ,,Warning messages:" címkéj a figyelmezu u tetések, a ,,Passes:" címkéj pedig a menetek számát mutatja; a ,,None" jelenti azt, ha nincs u hiba. Sikeres fordítás esetén elkészül a PELDA.OBJ állomány (ez a tárgykód). Most következik a szerkesztés. Ehhez a következ utasítást kell kiadni: o TLINK PELDA Ennek hatására (ha nem jelenik meg semmilyen figyelmeztet - vagy hibaüzenet) elkészül a o futtatható állomány, ami a PELDA.EXE nevet kapja. Ha a szerkesz a ,,Warning: No stack" üzenetet jeleníti meg, az azt jelenti, hogy nem o hoztunk létre a programban vermet. Ez elég gyakori hiba a tapasztalatlan Assembly-programozóknál. Mivel veremre mindenképpen szüksége van a programnak, sürg sen javítsuk ki mulasztásunkat! o

8. fejezet

Vezérlési szerkezetek megvalósítása
Ennyi elmélet után ideje, hogy belefogjunk az Assembly programok írásába! El ször néo hány példán keresztül megnézzük, hogy kódolható le néhány gyakran használt vezérlési szerkezet Assemblyben.

8.1. Szekvenciális vezérlési szerkezet
A processzor mindig szekvenciális utasítás-végrehajtást alkalmaz, hacsak nem használunk valamilyen vezérlésátadó utasítást. Lássuk hát els Assembly programunkat, ami nem sok haszo nos dolgot csinál, de kezdésnek megteszi.
Pelda1.ASM:
MODEL SMALL .STACK ADAT Kar Szo Szam Ered ADAT KOD @Start: MOV MOV MOV MOV XOR ADD SUB MOV CX,ADAT DS,CX BL,[Kar] AX,[Szo] BH,BH AX,BX AL,[Szam] [Ered],AX SEGMENT DB DW DB DW ENDS

'a' "Ha" 12h ?

SEGMENT ASSUME CS:KOD,DS:ADAT

8.1. SZEKVENCIÁLIS VEZÉRLÉSI SZERKEZET

38
AX,4C00h 21h @Start

KOD

MOV INT ENDS END

A programban három szegmenst hozunk létre: egy ADAT nev t az adatoknak, egy KOD u nev t az utasításoknak, illetve egy vermet. Utóbbit a .STACK egyszer sített szegmensdefiníciós u u direktíva készíti el, a szegmens neve STACK, mérete 0400h (1 Kbájt) lesz. Ezt a direktívát egészíti ki a MODEL, itt a memória-modellnek SMALL-t írtunk el , ami annyit tesz, hogy a o kód- és az adatterület mérete külön-külön max. 64 Kbájt lehet. Szegmensdefiníciót a szegmens nevéb l és a SEGMENT direktívából álló sorral kezdünk, o ezt követi a szegmens tartalmának leírása. Lezárásként a szegmens nevét és az azt követ ENDS o foglalt szót tartalmazó sort írjuk. Azaz:
Név definíciók Név SEGMENT {attribútumok} ENDS

A SEGMENT kulcsszó után a szegmens jellemz it meghatározó különféle dolgokat írhatunk o még, ezek a szegmens illeszkedését (alignment), kombinálását (combine type), osztályát (class), operandusméretét (operand size) és hozzáférési módját (access mode) határozzák meg. Ezekkel egyel re még ne tör djünk. o o Az adatszegmensben négy változót hozunk létre. Ebb l háromnak (Kar, Szo és Szam) kezo deti értéket is adunk, ezek tehát inkább hasonlítanak a Pascal típusos konstansaihoz. Változónak így foglalhatunk helyet:
Név Dx KezdetiÉrték<,KezdetiÉrték>

ahol Dx helyén DB, DW, DD, DF, DQ és DT állhat (jelentésük Define Byte, Word, Doubleword, Farword, Quadword és Ten byte unit). Ezek sorban 1, 2, 4, 6, 8 ill. 10 bájtnyi területet foglalnak le a Név nev változónak. A kezdeti érték megadásánál írhatunk közönséges számokat (12h), u karakter-konstansokat ('a'), sztring-konstansokat ("Ha"), ezek valamilyen kifejezését, illetve ha nem akarunk értéket adni neki, akkor egy ,,?"-et is. A kérd jellel definiált változókat a lego több assembler 00h-val tölti fel, de az is megoldható, hogy ezeket a területeket ne tárolja el a futtatható állományban, s indításkor ezek helyén valami véletlenszer érték legyen. Vessz vel u o elválasztva több értéket is felsorolhatunk, ekkor mindegyik értékhez egy újabb terület lesz lefoglalva. Így pl. a következ példa 10 bájtot foglal le, a terület elejére pedig a Valami címke fog o mutatni:
Valami DB 1,2,'#',"Szöveg",6+4

A kódszegmens megnyitása után jeleznünk kell az assemblernek, hogy ez valóban ,,kódszegmens", illetve meg kell adnunk, hogy melyik szegmens szerint számolja a memóriahivatkozások offszetcímét. Mindezeket az ASSUME direktívával intézzük el. Az ASSUME direktíva szintaxisa:
ASSUME ASSUME SzegReg:SzegNév<,SzegReg:SzegNév> NOTHING

8.1. SZEKVENCIÁLIS VEZÉRLÉSI SZERKEZET

39

A SzegReg valamilyen szegmensregisztert jelent, a SzegNev helyére pedig egy definiált szegmens vagy szegmenscsoport nevét, vagy a NOTHING kulcsszót írhatjuk. Utóbbi esetben az adott szegmensregisztert egyetlen szegmenshez sem köti hozzá, és nem tételez fel semmit sem az adott regiszterrel kapcsolatban. Az ASSUME NOTHING minden el z hozzárendelést mego o szüntet. Rögtön ezután egy címke következik, aminek a @Start nevet adtuk. (A ,,kukac" most csak arra utal, hogy ez nem változó-, hanem címkeazonosító. Ennek használata csak megszokás kérdése, de ebben a jegyzetben az összes címke nevét a ,,@" karakterrel fogjuk kezdeni.) Rendeltetése az, hogy a program indulásának helyét jelezze, erre az információra ugyanis az assemblernek, linkernek és az operációs rendszernek is szüksége van. A címke önmagában nem lesz elég, az indulási cím kijelölését a kés bb leírt END direktíva fogja elvégezni. o Az els utasítást, ami végrehajtható gépi kódot generál, a MOV (MOVe data) képviseli. Ez o két operandussal rendelkezik: az els a cél, a második a forrás (ez persze az összes kétopeo randusú utasítást jellemzi). M ködése: egyszer en veszi a forrás tartalmát, és azt bemásolja u u (eltárolja) a cél op. területére. Eközben a forrás tartalmát, ill. a flag-eket békén hagyja, csak a cél tartalma módosul(hat). Operandusként általános regiszteren és memóriahivatkozáson kívül konstans értéket (kifejezést) és szegmensregisztert is megadhatunk. Az els két sor az adatszegmens-regiszter (DS) tartalmát állítja be, hogy az általunk létrehoo zott ADAT-ra mutasson. Ezt nem teszi meg helyettünk az assembler annak ellenére sem, hogy az ASSUME direktívában már szerepel a DS:ADAT el írás. Látható, hogy az ADAT szegmens o címét el ször betöltjük egy általános regiszterbe (CX), majd ezt bemozgatjuk DS-be. Viszont o a MOV DS,ADAT utasítás érvénytelen lenne, mert az Intel 8086-os processzor csak memória vagy általános regiszter tartalmát tudja szegmensregiszterbe átrakni, és ez a fordított irányra is vonatkozik (mellesleg pl. a MOV 1234h,DS utasításnak nem lenne semmi értelme). Most jegyezzünk meg egy fontos dolgot: CS tartalmát ilyen módon nem tölthetjük fel (azaz a MOV CS, . . . forma érvénytelen), csak a vezérlésátadó utasítások változtathatják meg CS-t! Olvasni viszont lehet bel le, így pl. a MOV SI,CS helyes utasítás lesz. o A következ sor a Kar változó tartalmát BL-be rakja. Látható, hogy a memóriahivatkozást o a szögletes zárójelpár jelzi. Ez egyébként jó példa a közvetlen címzésre, a sort ugyanis az assembler a MOV BL,[0000h] gépi utasításra fordítja le. Ha jobban megnézzük, 0000h pont a Kar változó ADAT szegmensbeli offszetje. Ezért van tehát szükség az ASSUME direktíva megadására, mert különben az assembler nem tudta volna, hogy a hivatkozás mire is vonatkozik. AX-be hasonló módon a Szo változó értéke kerül. A XOR BH,BH kicsit trükkös dolgot m vel. Ha visszaemlékszünk a KIZÁRÓ VAGY értéku táblázatára, akkor könnyen rájöhetünk, hogy ha egy számot önmagával hozunk KIZÁRÓ VAGY kapcsolatba, akkor minden esetben nullát kapunk. Így tehát ez a sor megfelel a MOV BH,00h utasításnak, de ezt írhattuk volna még SUB BH,BH-nak is. Tehát már háromféleképpen tudunk kinullázni egy regisztert! Hogy végre számoljunk is valamit, az ADD AX,BX összeadja a BX tartalmát az AX tartalmával, majd az eredményt az AX-be teszi. Röviden: hozzáadja BX-et az AX regiszterhez. A m velet elvégzése során beállítja az OF, SF, ZF, AF, PF és CF flag-eket. u Kitalálható, hogy a következ sorban a Szam változó tartalmát vonjuk le az AL regiszterb l, o o a SUB (SUBtract) ugyanis a forrás tartalmát vonja ki a célból. Mind az ADD, mind a SUB utasításra igaz, hogy operandusként általános regisztert, memóriahivatkozást és közvetlen (konstans) értéket kaphatnak. Számolásunk eredményét el is kell tenni, ezt végzi el a MOV [Ered],AX sor. Ha befejeztük dolgunkat, és ,,ki akarunk lépni a programból", akkor err l az aktuális operáo ciós rendszert (ez most a DOS) kell értesíteni, s az majd cselekedni fog. Ezt végzi el programunk

8.2. SZÁMLÁLÁSOS ISMÉTLÉSES VEZÉRLÉS

40

utolsó két sora. Magyarázatképpen csak annyit, hogy a 21h szoftver-megszakítás alatt érhetjük el a DOS funkcióit, ennek 4Ch számú szolgáltatása jelenti a programból való kilépést (vagy másképpen a program befejezését, terminálását). A szolgáltatás számát AH-ban kell megadnunk, AL-be pedig egy visszatérési értéket, egyfajta hibakódot kell írni (ez lesz az ERRORLEVEL nev változó értéke a DOS-ban). A megszakítást az egyoperandusú INT (INTerrupt u request) utasítás váltja ki, s ezzel a program futása befejez dik. Az INT operandusa csak egy o bájt méret közvetlen érték lehet. A megszakítások használatát kés bb tárgyaljuk, most egyeu o l re fogadjuk el, hogy így kell befejezni a programot. (Persze másképpen is lehetne, de most o így csináljuk.) Az aktuális forrásfájlt az END direktívával kell lezárni, különben az assembler morcos lesz. Ha ez a f forrásfájl, azaz ez tartalmazza azt a programrészt, aminek el kell indulnia a program o betöltése után, akkor az END után egy címke nevének kell szerepelnie. Az itt megadott címre fog adódni a vezérlés a futtatható programfájl betöltése után. Vajon mi lett a számolás eredménye? BL-be az 'a' karaktert raktuk, ennek ASCII kódja 61h. A Szo értékének megadásához tudni kell, hogy a sztring-konstansokat is helyiértékes rendszerben értelmezi az assembler, 256-os alapú számként tekintve oket. Ezért a Szo tartalma 4861h. Az összeadás után így 4861h + 61h = 48C2h lesz AX-ben. Ebb l még levonjuk a Szam o tartalmát (12h), s ezzel kialakul a végeredmény: 48B0h.

8.2. Számlálásos ismétléses vezérlés
Magas szint nyelvekben igen gyakran alkalmaznak olyan ciklusokat, ahol a ciklusváltozó u egy bizonyos tartományt fut be, és a ciklus magja a tartomány minden értékére lefut egyszer. Ezt valósítják meg a különféle FOR utasítások. Assemblyben is mód nyílik számlálásos ismétléses vezérlésre, viszont van néhány megkötés a többi nyelvvel szemben: ciklusváltozónak csak CX használható, és nem adható meg a ciklusváltozó ismétlési tartománya, csak a lefutások számát írhatjuk el . o A következ program két vektor (egydimenziós tömb) tartalmát összegzi egy harmadik veko torba. Az egyik vektor el jeles bájtokból, míg a másik el jeles szavakból fog állni. o o
Pelda2.ASM:
MODEL SMALL .STACK ADAT ElemSzam Vekt1 Vekt2 Eredm ADAT KOD SEGMENT EQU DB DW DW ENDS

5 6,-1,17,100,-8 1000,1999,-32768,4,32767 ElemSzam DUP (?)

SEGMENT ASSUME CS:KOD,DS:ADAT

@Start: MOV MOV AX,ADAT DS,AX

8.2. SZÁMLÁLÁSOS ISMÉTLÉSES VEZÉRLÉS

41
CX,ElemSzam BX,BX SI,BX AL,[Vekt1+BX] AX,[Vekt2+SI] [Eredm+SI],AX BX SI,[SI+2] @Ciklus AX,4C00h 21h @Start

MOV XOR MOV @Ciklus: MOV CBW ADD MOV INC LEA LOOP MOV INT ENDS END

KOD

Az els újdonságot az adatszegmensben szerepl EQU (define numeric EQUate) kulcsszó o o képviseli. Ez a fordítási direktíva numerikus konstanst (pontosabban helyettesít makrót) tud o létrehozni, ezért leginkább a C nyelv #define utasításához hasonlítható. Hatására az ElemSzam szimbólum minden el fordulását az EQU jobb oldalán álló kifejezés értékével (5) fogja helyeto tesíteni az assembler. Az eredmény vektor nyilván ugyanolyan hosszú lesz, mint a két kiinduló vektor, típusa szerint szavas lesz. Számára a helyet a következ képpen is lefoglalhattuk volna: o
Eredm DW 0,0,0,0,0

Ez a megoldás azonban nem túl rugalmas. Ha ugyanis megváltoztatjuk az ElemSzam értékét, akkor az Eredm definíciójához is hozzá kell nyúlni, illetve ha nagy az elemek száma, akkor sok felesleges 0-t vagy kérd jelet kell kiírnunk. Ezeket a kényelmetlenségeket szünteti meg a dupo likáló operátor. A DUP (DUPlicate) hatására az operátor bal oldalán álló számú (ElemSzam), adott típusú (DW) elemekb l álló terület lesz lefoglalva, amit az assembler a DUP jobb oldalán o zárójelben szerepl kifejezéssel (most ,,?") inicializál (azaz ez lesz a kezd érték). Ez a módszer o o tehát jóval leegyszer síti a nagy változóterületek létrehozását is. u A ciklusváltozó szerepét a CX regiszter tölti be, ami nem véletlen, hiszen a regiszter neve is innen származik (Counter). A vektorelemek elérésére most nem használhatunk közvetlen címzést, hiszen azzal csak a vektorok legels elemét tudnánk kiválasztani. Ehelyett két megoldás kínálkozik: minden adato területhez hozzárendelünk egy-egy pointert (mutatót), vagy pedig valamilyen relatív címzést alkalmazunk. A pointeres megoldásnak akkor van értelme, ha mondjuk egy olyan eljárást írunk, ami a célterületnek csak a címét és elemszámának méretét kapja meg. Pointer alatt itt most rövid, szegmensen belüli mutatót értünk, ami egy offszet érték lesz. Pointereket csak bázis- vagy indexregiszterekben tárolhatunk, hiszen csak ezekkel végezhet memóriahivatkozás. o A másik módszer lehet vé teszi, hogy a változóinkat ténylegesen tömbnek tekinthessük, o és ennek megfelel en az egyes elemeket indexeléssel (tömbelem-hivatkozással) érhessük el. o Ehhez a megoldáshoz szükség van a tömbök bázisára (kezd címére), illetve az elemkiválaszo tást megvalósító tömbindexre. A tömbök kezd címét most konkrétan tudjuk, így ez konstans o numerikus érték lesz. A tömbindexet tetszésünk szerint tárolhatjuk bármely bázis- vagy indexregiszterben. Mivel minden vektorban elemr l-elemre sorban haladunk, valamint a Vekt2 o

8.2. SZÁMLÁLÁSOS ISMÉTLÉSES VEZÉRLÉS

42

és az Eredm vektorok is szavasak, ezért most csak kett tömbindexre van szükségünk. BX a o Vekt1-et indexeli, és egyesével kell növelni. SI-t ezzel szemben Vekt2 és Eredm indexelésére is használjuk, növekménye 2 bájt lesz. Vigyázzunk, az itt leírt tömbelem-hivatkozás csak alakilag hasonlít a magas szint nyelvek u tömbindexelésére! Míg az utóbbiaknál a hivatkozott tömbelem sorszámát kell megadni indexként, addig Assemblyben a tömbelemnek a tömb (vagy általában a terület) elejéhez képesti relatív címét használjuk. Assemblyben a ciklus magja a ciklus kezdetét jelz címkét l (most @Cimke) a ciklusképz o o o utasításig (ez lesz a LOOP) tart. A Vekt1 következ elemét olvassuk ki el ször AL-be, bázisrelatív címzést használva. Azo o után ehhez kéne hozzáadnunk a Vekt2 megfelel elemét. A két vektor elemmérete azonban o eltér , egy bájtot pedig nem adhatunk hozzá egy szóhoz. Ez megoldható, ha a bájtot átkono vertáljuk szóvá. Az operandus nélküli CBW (Convert Byte to Word) utasítás pont ezt teszi, az AL-ben lev értéket el jelesen kiterjeszti AX-be. Így már elvégezhet az összeadás, ami után az o o o eredményt is a helyére tesszük. Az INC (INCrement) utasítás az operandusát növeli meg 1-gyel, de természetesen nem lehet közvetlen érték ez az operandus. M ködése az ADD . . . ,1 utasítástól csak annyiban tér el, u hogy nem változtatja meg a CF értékét, és ez néha fontos tud lenni. Az INC párja a DEC (DECrement), ami eggyel csökkenti operandusát. SI-t többféleképpen is megnövelhetnénk 2-vel: 1.
INC INC SI SI

2.
ADD SI,2

Mindkét megoldásnak van egy pici szépséghibája: az els feleslegesen ismétel meg egy utao sítást, a második viszont túl nagy kódot generál (4 bájtot), és a flag-eket is elállítja. Ha kicsi a hozzáadandó érték (mondjuk el jelesen 1 bájtos), akkor érdemesebb a LEA (Load Effective o Address) utasítást használni. Ez a forrás memóriaoperandus tényleges címét (effective address) tölti be a céloperandusba, ami csak egy 16 bites általános regiszter lehet. Az összes flag-et békén hagyja. Ismétlésképpen: tényleges cím alatt a használt címzési mód által jelölt, meghatározott memóriacímet (offszetet) értjük. Így a LEA SI,[SI+2] utasítás a DS:(SI +2) cím memóriarekesz u offszetcímét tölti be SI-be, azaz 2-vel megnöveli SI-t. Ez így csak 3 bájtos utasítást eredményez. Az utasítás jelent sége jobban látszik, ha bonyolultabb címzésmódot használunk: o
LEA AX,[BX+DI-100]

Itt AX-be egy háromtagú összeg értéke kerül, s mindezt úgy hajthatjuk végre, hogy más regiszterre nem volt szükségünk, továbbá ez az utasítás is csak három bájtot foglal el. A tényleges ciklusképzést a LOOP utasítás végzi. M ködése: csökkenti CX-et, majd ha az u zérussá válik, akkor a LOOP utáni utasításra kerül a vezérlés, különben elugrik az operandus által mutatott memóriacímre. A csökkentés során egyetlen flag értéke sem változik meg. A

8.3. SZELEKCIÓS VEZÉRLÉS

43

LOOP ú.n. relatív ugrást használ, ami azt jelenti, hogy a cél címet a LOOP után következ o

utasítás elejéhez képest relatívan kell megadni (ez csak a gépi kód szinten számít, Assemblyben nyugodtan használhatunk címkéket). Ezt a relatív címet 1 bájton tárolja el, így a -128 ­ +127 bájtnyi távolságra lev címekre tudunk csak ciklust szervezni. Ha az operandus által mutatott o cím túl messze lenne, akkor az assembler hibát fog jelezni. Ilyenkor csak azt tehetjük, hogy más módon (pl. feltételes és feltétel nélküli ugrások kombinációjával) oldjuk meg a problémát. A szabályos ciklusokban egyébként csak visszafelé mutató címet szoktunk megadni a LOOP után (tehát a relatív cím negatív lesz). Fontos még tudni, hogy a LOOP el ször csökkenti CXo et, s csak ezután ellen rzi, hogy az nullává vált-e. Így ha a ciklusba belépéskor CX zéró volt, o akkor nem egyszer (és nem is nullaszor), hanem 65536-szor fog lefutni ciklusunk! A LOOP utasítással megírt ciklus tehát legalább 1-szer mindenképpen lefut. A LOOP utasításnak még két változata létezik. A LOOPE, LOOPZ (LOOP while Equal/Zero/ZF = 1) csökkenti CX-et, majd akkor ugrik a megadott címre, ha CX 0000h és ZF = 1. Ha valamelyik vagy mindkét feltétel nem teljesül, a LOOP utáni utasításra kerül a vezérlés. A két mnemonik ugyanazt a m veleti kódot generálja. Hasonlóan a LOOPNE, LOOPNZ (LOOP u while Not Equal/Not Zero/ZF = 0) utasítás CX csökkentése után akkor ugrik, ha CX 0000h és ZF = 0 is teljesül.

8.3. Egyszeru és többszörös szelekciós vezérlés
Ez a vezérlési szerkezet egy vagy több feltétel teljesülése vagy nem teljesülése szerinti elágazást tesz lehet vé a program menetében. Megfelel az IF . . . THEN . . . ELSE utasítások láncoo latának. A megoldandó probléma: konvertáljunk át egy karaktersorozatot csupa nagybet sre. Az u ékezetes bet kkel most nem tör dünk, a szöveget pedig az ASCII 00h kódú karakter fogja u o lezárni.
Pelda3.ASM:
MODEL SMALL .STACK ADAT Szoveg ADAT KOD SEGMENT DB "Ez egy PELDA szoveg." DB "Jo proci a 8086-os!",00h ENDS SEGMENT ASSUME CS:KOD,DS:ADAT

@Start: MOV MOV PUSH POP LEA MOV CLD AX,ADAT DS,AX DS ES SI,[Szoveg] DI,SI

8.3. SZELEKCIÓS VEZÉRLÉS

44

@Ciklus: LODSB OR JZ CMP JB CMP JA SUB @Tarol: STOSB JMP @Vege: MOV INT ENDS END AX,4C00h 21h @Start @Ciklus AL,AL @Vege AL,'a' @Tarol AL,'z' @Tarol AL,'a'-'A'

KOD

A program elején két ismer s utasítással is találkozhatunk, ezek a verem tárgyalásánál már o említett PUSH és POP. Mindkett egyoperandusú, s ez az operandus nemcsak általános reo giszter vagy memóriahivatkozás, de szegmensregiszter is lehet. 8 bites regisztert viszont nem fogadnak el, mivel a verem szavas szervezés . A programban szerepl PUSH-POP pár tuu o lajdonképpen DS tartalmát tölti ES-be, amit rövidebben írhattunk volna MOV ES,AX-nek is, mindössze szemléltetni akarjuk, hogy így is lehet értéket adni egy szegmensregiszternek. Bár nem tartozik szorosan a kit zött feladathoz, két probléma is felmerül ezzel a két utau sítással kapcsolatban. Az egyik, hogy vajon mit csinál a PUSH SP utasítás? Nos, a 8086-os processzor esetén ez SP 2-vel csökkentése után SP új (csökkentett) értékét teszi le a verem SS:SP címére, míg a kés bbi processzorok esetén a régi érték kerül eltárolásra. Furcsa egy o megoldás, az biztos. . . Mindenesetre ezzel nem kell foglalkoznunk, csak egy érdekesség. A másik dolog, amit viszont fontos megjegyezni, az az, hogy a POP CS utasítást a MOV CS, . . . -hez hasonlóan nem szereti sem az assembler, sem a processzor. Ha belegondolunk hogy ez mit jelentene, láthatjuk, hogy feltétel nélküli vezérlésátadást valósítana meg, de úgy, hogy közben csak CS értéke változna, IP-é nem! Ez pedig veszélyes és kiszámíthatatlan eredményekkel járna. Így a CS nem lehet a POP operandusa. (Igazság szerint a 8086-os processzor értelmezni tudja ezt az utasítást, aminek gépi kódja 0Fh, viszont a kés bbi processzorokon ennek a kódnak o más a szerepe. Ezért és az el bb említettek miatt ne használjuk!) o A következ három utasítás a sztringkezel utasításokat (LODSB és STOSB) készíti el . o o o A forrássztring címét DS:SI fogja tartalmazni, így a Szoveg offszetjét betöltjük SI-be. Megjegyezzük, hogy a TASM ezt az utasítást MOV SI,OFFSET [Szoveg]-ként fogja lefordítani, talán azért, mert ez kicsit gyorsabb lesz, mint az eredeti változat. A célsztring az ES:DI címen fog elhelyezkedni. Most helyben fogunk konvertálni, így DI-t egyenl vé tesszük SI-vel. o Az operandus nélküli CLD (CLear Direction flag) a DF flag-et törli. Ennek szükségessége rögvest kiderül. A LODSB (LOaD String Byte) a DS:SI címen lev bájtot betölti AL-be, majd SI-t eggyel o növeli, ha DF = 0, ill. csökkenti, ha DF = 1. Másik alakja, a LODSW (LOaD String Word) AX-be tölti be a DS:SI-n lev szót, és SI-t 2-vel növeli vagy csökkenti DF-t l függ en. Egyetlen o o o flag-et sem változtat meg. Az OR AL,AL utasítás ismét egy trükköt mutat be. Ha egy számot önmagával hozunk lo-

8.3. SZELEKCIÓS VEZÉRLÉS

45

gikai VAGY kapcsolatba, akkor maga a szám nem változik meg. Cserébe viszont CF, AF és OF törl dik, SF, ZF és PF pedig az operandusnak megfelel en módosulnak. Itt most annak o o tesztelésére használtuk, hogy AL zérus-e, azaz elértük-e a sztringünk végét. Ezt megtehettük volna a kés bb szerepl CMP AL,00h utasítással is, csak így elegánsabb. o o Elérkeztünk a megoldás kulcsához, a feltételes ugrásokhoz (conditional jumps). Ezek olyan vezérlésátadó utasítások, amik valamilyen flag (vagy flag-ek) állása alapján vagy elugranak az operandus szerinti címre, vagy a következ utasításra térnek. Összesen 17 db. van o bel lük, de mnemonikból ennél több van, mivel némelyik egynél több elnevezés alatt is elo érhet . A táblázatban egy sorban lev mnemonikok ugyanazt az utasítást képviselik. Három o o csoportba oszthatók: el jeles aritmetikai, el jeltelen aritmetikai és egyéb feltételes ugrások. o o El ször jöjjenek az el jeles változatok (8.1. táblázat)! o o 8.1. táblázat. El jeles aritmetikai feltételes ugrások o

Mnemo.
JL, JNGE JNL, JGE JLE, JNG JNLE, JG

Hatás Ugrás, ha kisebb/nem nagyobb vagy egyenl o Ugrás, ha nem kisebb/nagyobb vagy egyenl o Ugrás, ha kisebb vagy egyenl /nem nagyobb o Ugrás, ha nem kisebb vagy egyenl /nagyobb o

A mnemonikokat tehát úgy képezzük, hogy a ,,J" bet t (ami a Jump if . . . kifejezést rövidíti) u egy, kett vagy három bet s rövidítés követi. Némi angol tudással könnyebben megjegyezhet k o u o az egyes változatok, ha tudjuk, mit jelölnek az egyes rövidítések: · L ­ Less · G ­ Greater · N ­ Not · LE ­ Less or Equal · GE ­ Greater or Equal Most nézzük az el jel nélküli ugrásokat (8.2. táblázat)! o 8.2. táblázat. El jeltelen aritmetikai feltételes ugrások o

Mnemonik
JB, JNAE, JC JNB, JAE, JNC JBE, JNA JNBE, JA

Hatás Ugrás, ha kisebb/nem nagyobb vagy egyenl /CF = 1 o Ugrás, ha nem kisebb/nagyobb vagy egyenl /CF = 0 o Ugrás, ha kisebb vagy egyenl /nem nagyobb o Ugrás, ha nem kisebb vagy egyenl /nagyobb o

Az alkalmazott rövidítések: · B ­ Below · C ­ Carry · A ­ Above

8.3. SZELEKCIÓS VEZÉRLÉS

46

· BE ­ Below or Equal · AE ­ Above or Equal Mint várható volt, a JB és JC ugyanazt jelentik, hiszen mindkett azt fejezi ki, hogy a legutolsó o m velet el jel nélküli aritmetikai túlcsordulást okozott. u o Lássuk a megmaradt további ugrásokat (8.3. táblázat)! 8.3. táblázat. Speciális feltételes ugrások

Mnemo.
JE, JZ JNE, JNZ JO JNO JS JNS JP, JPE JNP, JPO JCXZ

Hatás Ugrás, ha egyenl /ZF = 1 o Ugrás, ha nem egyenl /ZF = 0 o Ugrás, ha OF = 1 Ugrás, ha OF = 0 Ugrás, ha SF = 1 Ugrás, ha SF = 0 Ugrás, ha PF = 1/páros paritás Ugrás, ha PF = 0/páratlan paritás Ugrás, ha CX = 0000h

A rövidítések magyarázata pedig: · E ­ Equal · Z ­ Zero · O ­ Overflow · S ­ Sign · P ­ Parity · PE ­ Parity Even · PO ­ Parity Odd · CXZ ­ CX is Zero (vagy CX equals Zero) Mint látható, a JCXZ kakukktojás a többi ugrás között, mivel nem egy flag, hanem a CX regiszter állása szerint cselekszik. Mindegyik feltételes ugró utasítás egyetlen operandusa a cél memóriacím, de ez is el jeles relatív címként 1 bájtban tárolódik el, a LOOP utasításhoz hasono lóan. Pontosan ezen megkötés miatt található meg mindegyik feltételnek (a JCXZ-t kivéve) a negált párja is. A következ példát ugyanis nem fogja lefordítani az assembler (a TASM-ra ez o nem teljesen igaz, a JUMPS és NOJUMPS direktívákkal megadható, hogy magától kijavítsa-e az ilyen helyzeteket, avagy utasítsa vissza):
XOR JZ ... DB ... AX,AX @Cimke 200 DUP (?)

KamuAdat @Cimke:

8.3. SZELEKCIÓS VEZÉRLÉS

47

A 200 bájtos területfoglalás biztosítja, hogy a JZ utasítást követ bájt messzebb legyen a @Cimke o címkét l, mint a megengedett +127 bájt. Az ugrást ráadásul végre kell hajtani, mivel a XOR o AX,AX hatására ZF = 1 lesz. Ezt a hibás szituációt feloldhatjuk, ha a JZ . . . helyett JNZ-JMP párost használunk (a JMP leírása kicsit lentebb lesz):
XOR JNZ JMP @KamuCimke: KamuAdat @Cimke: ... DB ... 200 DUP (?) AX,AX @KamuCimke @Cimke

A példaprogramhoz visszatérve, az OR AL,AL és a JZ . . . hatására megoldható, hogy az algoritmus leálljon, ha elértük a sztring végét. Most jön maga a szelekció: el kell dönteni, hogy az aktuális karaktert (ami AL-ben van) kell-e konvertálni. Konverziót kell végezni, ha 61h AL 7Ah ('a' kódja 61h, 'z' kódja 7Ah), különben változatlanul kell hagyni a karaktert. El ször azt nézzük meg, hogy AL < 61h o teljesül-e. Ha igen, akkor nem kell konverzió. Különben ha AL > 7Ah, akkor ugyancsak nincs konverzió, különben pedig elvégezzük az átalakítást. A leírtakat CMP-Jccc utasításpárosokkal oldjuk meg. (A ,,ccc" egy érvényes feltételt jelöl. A ,,Jccc" jelölés ezért valamilyen feltételes ugrás mnemonikja helyett áll.) A CMP (CoMPare) utasítás kiszámítja a céloperandus és a forrásoperandus különbségét, beállítja a flag-eket, az operandusokat viszont békén hagyja (és eldobja az eredményt is). Operandusként általános regisztert, memóriahivatkozást és konstans értéket adhatunk meg. A konverziót az egyetlen SUB AL,'a'-'A' utasítás végzi el. Akár volt konverzió, akár nem, az algoritmus végrehajtása a @Tarol címkét l folytatódik. o Megfigyelhetjük, hogy ez a rész közös rész a szelekció mindkét ágában (t.i. volt-e konverzió avagy nem), így felesleges lenne mindkét esetben külön leírni. Ehelyett azt tesszük, hogy ha nem kell konvertálni, akkor elugrunk a @Tarol címkére, különben elvégezzük a szükséges átalakítást, és ,,rácsorgunk" erre a címkére. Az ilyen megoldások igen gyakoriak az Assembly programokban, használatuk rövidebbé, gyorsabbá teszi azokat, az algoritmusok pedig áttekinthet bbek lesznek általuk. o Most jön a kérdéses karakter eltárolása. Ezt egy másik sztringkezel utasítás, a STOSB o (STOre String Byte) intézi el. M ködése: AL-t eltárolja az ES:DI címre, majd DI-t megnöveli u eggyel, ha DF = 0, illetve csökkenti, ha DF = 1. A STOSW (STOre String Word) utasítás, hasonlóan a LODSW-hez, AX-szel dolgozik, és DI-t 2-vel növeli vagy csökkenti. Ezután vissza kell ugranunk a @Ciklus címkére, mert a többi karaktert is fel kell dolgozni a sztringünkben. Erre szolgál a JMP (JuMP) feltétel nélküli ugrás (unconditional jump). M köu dése: az operandusként megadott címre állítja be IP-t (van egy másik, távoli formája is, ekkor CS-t is átállítja), és onnan folytatja a végrehajtást. Érdekes, hogy a JMP is relatív címként tárolja el a cél memóriacímét, viszont a feltételes ugrásokkal és a LOOP-pal ellentétben a JMP 16 bites relatív címet használ, így a -32768 ­ +32767 bájt távolságban lev címekre tudunk o el re-hátra ugrálni. (Ez csak a közeli, ugyanazon kódszegmensen belüli ugrásra vonatkozik, de o err l majd kés bb.) o o Ha jobban megnézzük, észrevehetjük, hogy az algoritmus elején szerepl OR AL,AL // JZ o @Vege és az utóbbi JMP @Ciklus utasítások egy hurok vezérlési szerkezetet írnak el , amiben o

8.4. ELJÁRÁSVEZÉRLÉS

48

az OR-JZ páros alkotja a kilépési feltételt, a JMP pedig maga a ciklusszervez utasítás (t.i. o végtelen ciklust valósít meg). De ha jobban tetszik, ezt tekinthetjük akár el feltételes vezérlési o szerkezetnek is (WHILE . . . DO ciklus). A @Vege címke elérésekor a kiinduló sztringünk már nagybet sen virít a helyén. u

8.4. Eljárásvezérlés
A program eljárásokra és függvényekre (egyszóval szubrutinokra) bontása a strukturált programozás alapját képezi. Nézzük meg, hogyan használhatunk eljárásokat Assemblyben. A feladat: írjunk olyan eljárást, ami az AX-ben található el jel nélküli számot kiírja a képero ny re decimális alakban! o
Pelda4.ASM:
MODEL SMALL .STACK KOD SEGMENT ASSUME CS:KOD,DS:NOTHING PROC PUSH PUSH PUSH PUSH MOV XOR OR JZ XOR DIV PUSH INC JMP @CiklVege: MOV JCXZ @KiirCikl: POP ADD INT LOOP JMP @NullaVolt: MOV INT @KiirVege: POP DX DL,'0' 21h DX DL,'0' 21h @KiirCikl @KiirVege AH,02h @NullaVolt

DecKiir

AX BX CX DX BX,10 CX,CX AX,AX @CiklVege DX,DX BX DX CX @OsztCikl

@OsztCikl:

8.4. ELJÁRÁSVEZÉRLÉS

49
POP POP POP RET ENDP PROC PUSH PUSH MOV MOV INT MOV INT POP POP RET ENDP CX BX AX

DecKiir UjSor

AX DX AH,02h DL,0Dh 21h DL,0Ah 21h DX AX

UjSor @Start:

KOD

XOR CALL CALL MOV CALL CALL MOV CALL CALL MOV CALL CALL MOV INT ENDS END

AX,AX DecKiir UjSor AX,8086 DecKiir UjSor AX,8000h DecKiir UjSor AX,0FFFFh DecKiir UjSor AX,4C00h 21h @Start

Ez egy kicsit hosszúra sikeredett, de hát a probléma sem annyira egyszer . Nézzük el u o ször a f programot (azaz a @Start címke utáni részt)! Itt minden ismer snek t nik, kivéve a o o u CALL utasítást. Ez az egyoperandusú utasítás szolgál az eljárások végrehajtására, más szóval az eljáráshívásra (procedure call). M ködése: a verembe eltárolja az IP aktuális értékét (tehát u szimbolikusan végrehajt egy PUSH IP-t), majd IP-t beállítja az operandus által mutatott címre, és onnan folytatja az utasítások végrehajtását. A célcímet a JMP-hez hasonlóan 16 bites el jeles o relatív címként tárolja. (Létezik távoli formája is, ekkor CS-t is lerakja a verembe az IP el tt, o illetve beállítja a cél kódszegmenst is.) Az operandus helyén most egy eljárás neve áll (ami végül is egy olyan címke, ami az eljárás kezdetére mutat). Két eljárást írtunk, a DecKiir végzi el AX tartalmának kiírását, az UjSor pedig a képerny következ sorára állítja a kurzort. Tesztelés o o céljából a 0, 8086, 32768 és 65535 értékeket íratjuk ki. Most térjünk vissza a program elejére, ahol az eljárásokat helyeztük el (egyébként bárhol lehetnének, ez csak megszokás kérdése). Eljárást a következ módon definiálhatunk: o

8.4. ELJÁRÁSVEZÉRLÉS

50
Név eljárástörzs Név PROC ENDP {opciók}

Az eljárás törzsét (azaz annak m ködését leíró utasításokat) a PROC . . . ENDP direktívák (nem u pedig mnemonikok!) fogják közre. Van néhány olyan rész, ami a legtöbb eljárás törzsében közös. Az eljárás elején szokásos az eljárásban kés bb módosított regiszterek tartalmát a verembe o elmenteni, hogy azokat kés bb visszaállíthassuk, és a hívó program m ködését ne zavarjuk be o u azzal, hogy a regiszterekbe zagyvaságokat teszünk. Természetesen ha egy regiszterben akarunk visszaadni valamilyen értéket, akkor azt nem fogjuk elmenteni. Az eljárásból a hívóhoz visszatérés el tt a verembe berakott regiszterek tartalmát szépen helyreállítjuk, méghozzá pontosan a o berakás fordított sorrendjében. A DecKiir eljárásban a 4 általános adatregisztert fogjuk megváltoztatni, ezért ezeket szépen PUSH-sal berakjuk a verembe az elején, majd az eljárás végén fordított sorrendben POP-pal kivesszük. Bináris számot úgy konvertálunk decimálisra, hogy a kiinduló számot elosztjuk maradékosan 10-zel, és a maradékot szépen eltároljuk. Ha a hányados nulla, akkor készen vagyunk, különben a hányadost tekintve az új számnak, azon folytatjuk a 10-zel való osztogatást. Ha ez megvolt, akkor nincs más dolgunk, mint hogy az osztások során kapott maradékokat a megkapás fordított sorrendjében egymás mellé írjuk mint számjegyeket. Így tehát az els maradék o lesz a decimális szám utolsó jegye. Az osztót most BX-ben tároljuk, CX pedig az eltárolt maradékokat (számjegyeket) fogja számolni, ezért kezdetben kinullázzuk. Az osztásokat egy el feltételes ciklusba szervezve végezzük el. A ciklusnak akkor kell o megállnia, ha AX (ami kezdetben a kiírandó szám, utána pedig a hányados) nullává válik. Ha ez teljesül, akkor kiugrunk a ciklusból, és a @CiklVege címkére ugrunk. Ha AX 0000h, akkor osztanunk kell. A DIV (unsigned DIVision) utasítás szolgál az el jelo telen osztásra. Egyetlen operandusa van, ami 8 vagy 16 bites regiszter vagy memóriahivatkozás lehet. Ha az operandus bájt méret , akkor AX-et osztja el az adott operandussal, a hányadost u AL-be, a maradékot pedig AH-ba teszi. Ha szavas volt az operandus, akkor DX:AX-et osztja el az operandussal, a hányados AX-be, a maradék DX-be kerül. A 6 aritmetikai flag értékét elrontja. Nekünk most a második esetet kell választanunk, mert el fordulhat, hogy az els néhány osztás o o hányadosa nem fog elférni egy bájtban. Így tehát BX-szel fogjuk elosztani DX:AX-et. Mivel a kiinduló számunk csak 16 bites volt, a DIV viszont 32 bites számot követel meg, DX-et az osztás elvégzése el tt törölnünk kell. o A maradékokat a veremben fogjuk tárolni az egyszer bb kiíratás kedvéért, ezért DX-et beu rakjuk oda, majd a számlálót megnöveljük. Végül visszaugrunk az osztó ciklus elejére. A számjegyek kiírására a legegyszer bb módszert választjuk. Már említettük, hogy az u INT 21h-n keresztül a DOS szolgáltatásait érhetjük el. A 02h számú szolgáltatás (azaz ha AH = 02h) a DL-ben lev karaktert kiírja a képerny re az aktuális kurzorpozícióba. AH-ba o o ezért most berakjuk a szolgáltatás számát. Ha az eljárás hívásakor AX nulla volt, akkor az osztó ciklus egyszer sem futott le, hanem rögtön az elején kiugrott. Ekkor viszont CX = 0, hiszen így állítottuk be. Ezek hatására a JCXZ utasítás segítségével a @NullaVolt címkén folytatjuk az eljárás végrehajtását, ahol a 21hs szoftver-megszakítás segítségével kiírjuk azt az egy számjegyet, majd ,,rácsorgunk" a @KiirVege címkére. Ha nemzéró értékkel hívtuk meg eljárásunkat, akkor itt az ideje, hogy kiírjuk a CX db. jegyet a képerny re. Ezt egy egyszer számlálásos ismétléses vezérléssel (LOOP ciklussal) o u

8.4. ELJÁRÁSVEZÉRLÉS

51

elintézhetjük. Mivel a verem LIFO m ködés , így a legutoljára betett maradékot vehetjük ki u u legel ször, és ezt is kell els számjegyként kiírnunk. Az érvényes decimális jeggyé konvertálást o o az ADD DL,'0' utasítás végzi el, majd a karaktert megszakítás-hívással kiküldjük a monitorra. A kiírás végeztével szintén a @KiirVege címkére megyünk. Miután visszaállítottuk a módosított eredeti regiszterek tartalmát, vissza kell térnünk arra a helyre, ahonnan meghívták az eljárást. Erre szolgál a RET (RETurn) utasítás. Két változata van: ha nem írunk mellé operandust, akkor a veremb l kiszedi IP-t (amit el z leg a CALL rakott o o o oda), és onnan folytatja a végrehajtást. De írhatunk egy 16-bites közvetlen értéket (numerikus kifejezést) is operandusként, ekkor az IP kiszedése után ezt a számot hozzáadja SP-hez (olyan, mintha Szám/2 db. POP-ot hajtana végre), majd az új CS:IP címre adja a vezérlést. Mi most nem akarunk változtatni a veremmutatón, így az egyszer RET-tel visszatérünk a f programba. u o Az UjSor eljárás nagyon egyszer és rövid. A regiszterek elmentésén ill. visszaállításán u kívül csak két megszakítás-hívást tartalmaz, amik a 0Dh és 0Ah ASCII kódú karaktereket írják ki a képerny re. Az els karakter az ú.n. kocsivissza (CR ­ Carriage Return), a másik pedig a o o soremelés (LF ­ Line Feed). Ezek alkotják az új sor (new line) karakterpárt. Ha sok regisztert kell elmentenünk és/vagy visszaállítanunk, akkor fárasztó és felesleges minden egyes regiszterhez külön PUSH vagy POP utasítást írni. Ezt megkönnyítend , a TASM o lehet vé teszi, hogy egynél több operandust írjunk ezen utasítások után, az egyes operandusokat o egymástól szóközzel elválasztva. Így a PUSH AX BX CX DX // POP SI DI ES utasítások ekvivalensek a következ sorozattal: o
PUSH PUSH PUSH PUSH POP POP POP AX BX CX DX SI DI ES

9. fejezet

A Turbo Debugger használata
Az eddig megírt programjaink m ködését nem tudtuk ellen rizni, úgy pedig elég kényelu o metlen programozni, hogy nem látjuk, tényleg azt csinálja-e a program, amit elvárunk t le. o Ha valami rosszul m ködik, netán a számítógép semmire sem reagál (ezt úgy mondjuk, hogy u ,,kiakadt" vagy ,,lefagyott"), a hiba megkeresése egyszer en reménytelen feladat segítség nélu kül. Ezt a természetes igényt elégítik ki a különböz debugger (nyomkövet , de szó szerint o o ,,bogártalanító") szoftverek ill. hardverek. (Az elnevezés még a számítástechnika h skorszakáo ból származik, amikor is az egyik akkori számítógép m ködését valamilyen rovar zavarta meg. u Azóta hívják a hibavadászatot ,,bogárirtásnak".) A Turbo Assemblert és Linkert készít Borland cég is kínál egy szoftveres nyomkövet o o eszközt, Turbo Debugger (TD) néven. Most ennek használatával fogunk megismerkedni. A Turbo Debugger f tulajdonsága, hogy képes egy futtatható állományt (.EXE vagy .COM o kiterjesztéssel) betölteni, majd annak gépi kódú tartalmát Assembly forrásra visszafejteni. Ezt hívjuk disassemblálásnak (disassembly) vagy szebb magyar szóval a forrás visszafejtésének. A legszebb a dologban az, hogy a programban szerepl kód- és memóriahivatkozásokat konkrét o számok helyett képes az eredeti forrásban szerepl szimbólumokkal megjeleníteni. Ezenkívül o minden utasítást egyenként hajthatunk végre, ha akarjuk, többször is, s t megadhatjuk, hogy o a program végrehajtása egy bizonyos feltétel teljesülése esetén szakadjon meg. Figyelhetjük a memória tetsz leges területének tartalmát, a regiszterek és flag-ek értékeit, s mindezeket meg o is változtathatjuk. A program futását az eredeti forrásokon is képes követni. Szóval csupacsupa hasznos szolgáltatással bír, amikkel pont olyan kényelmesen figyelhetjük programunk tevékenységét, mintha mondjuk a Borland C fejleszt rendszerében (IDE) dolgoznánk. o Ahhoz hogy mindezt a kényelmet élvezhessük, nem kell mást tenni, mint: · minden forrást a /zi vagy /zd kapcsolóval lefordítani · a tárgykódokat a /v kapcsolóval összeszerkeszteni Ha ezt a két m veletet elvégeztük, akkor a .EXE állományunk (remélhet leg) tartalmazza az u o összes szimbolikus információt, amire a nyomkövetés során szükség lehet. A dolognak két hátránya van: egyrészt .COM programokba nem kerülhet nyomkövetési info, másrészt ez az adathalmaz az .EXE fájl méretét igencsak megnövelheti (el fordulhat, hogy o az eredeti többszöröse lesz a kimenet mérete). A legels példaprogramot (Pelda1.ASM) begépeltük és elmentettük PELDA.ASM néven, o majd lefordítottuk és linkeltük, az összes debug információt belerakva. A

53 TD PELDA.EXE parancs kiadása utáni állapotot tükrözi a 9.1. kép. A képerny tetején ill. alján a már megszokott menüsor és státuszsor találhatók. o A kép nagy részét elfoglaló munkaasztalon (desktop) helyezkednek el a különféle célt szolgáló ablakok. A legfels ablak (CPU ablak) több részre osztható. A bal fels sarok az aktuális kód dio o sassemblált változatát mutatja, és most éppen úgy van beállítva, hogy a forrás eredeti sorait is megjeleníti. A következ végrehajtandó sor a bal oldalon egy kis jobbra mutató háromszöggel o van megjelölve (ez most a CS:0009h cím sor). u Emellett az egyes regiszterek és flag-ek tartalma látható. Az el z állapothoz képest mego o változott dolgokat fehér színnel jelöli meg. A bal alsó sarok a memória egy adott területének tartalmát mutatja hexadecimális és ASCII alakban (ez az ú.n. memory dump). Végül a jobb alsó sarokban a verem aktuális állapotát szemlélhetjük meg. A veremmutatót (azaz a verem tetejét) szintén egy jobbra mutató sötét háromszög jelzi. A kép közepén lev nagyobb ablakban a forrásban követhetjük nyomon a program futását. o Itt mindig azt a fájlt látjuk, amihez az éppen végrehajtott kód tartozik. A következ végrehajo tandó sort a bal oldalon kis fehér háromszög mutatja. Az alatta látható, Stack címkéj ablak a különböz eljárás- és függvényhívások során a u o verembe rakott argumentumokat mutatja. A legalsó, keskeny ablak a Watches címkét viseli. Ennek megfelel en az általunk óhajtott o változók, memóriaterületek aktuális értékét követhetjük itt figyelemmel. A fels sorban található menürendszeren kívül minden ablakban el hívható egy helyi menü o o (local menu) az + billenty kombinációval, ahol az aktuális ablakban (vagy réu szablakban) rendelkezésre álló plusz szolgáltatásokat érhetjük el. Ilyenek pl. az utasítás assemblálása, regiszter tartalmának megváltoztatása, flag törlése, megfigyelend változó felvétele o a Watches listába stb. Az ablakok között az és + billenty kkel mozoghatunk, míg az aktuáu lis ablakon belül a és a + kombinációkkal lépkedhetünk a részablakok között. Most áttekintjük az egyes menük funkcióit. A File menü a szokásos állománym veleteket u tartalmazza, de itt kaphatunk információt a betöltött programról is. Az Edit menü szintén a gyakori másolás-beszúrás típusú funkciókat rejti. A View menü készlete igen gazdag. Innen nézhetjük meg a változókat (Variables), a CPU ablakot, másik programmodult, tetsz leges állományt és még sok minden mást. o A Run menüben, mint neve is sejteti, a futtatással kapcsolatos tevékenységek vannak összegy jtve, de itt állíthatók be a program futtatási (parancssoros) argumentumai is. Szinte az összes u itteni szolgáltatáshoz tartozik valamilyen gyorsbillenty (hot key) is. Néhány ezek közül: futu tatás , újraindítás +, adott sorig végrehajtás , lépésenkénti végrehajtás , CALL és INT utasítások átugrása . A Breakpoints menüvel a töréspontokat tarthatjuk karban. A töréspont egy olyan hely a kódban, ahol a program végrehajtásának valamilyen feltétel teljesülése esetén (vagy mindenképpen) meg kell szakadnia. Ilyenkor a vezérlést ismét visszakapjuk a TD képerny jével együtt, o és kedvünk szerint beavatkozhatunk a program menetébe. A Data menü az adatok, változók manipulálását segíti. Az Options menüben találhatók a TD beállításai. Ilyenek pl. a forrás nyelve (Language), helye (Path for source), képerny vel kapcsolatos dolgok (Display options). o

54

9.1. ábra. A Turbo Debugger képerny je o

55 A Window menü az ablakokkal való b vészkedésre jó, a Help menü pedig a szokásos súgót u tartalmazza. A TD egyébként nemcsak Assembly, de Pascal és C nyelv programot is képes nyomon u követni, és az ezekben a nyelvekben meglev összes adattípust is képes kezelni. o

10. fejezet

Számolás el jeles számokkal, o bitmuveletek
Ebben a fejezetben a gyakorlati problémák megoldása során gyakran el forduló feladatoko kal foglalkozunk. Teljes példaprogramokat most nem közlünk, a megoldás módszerét egy-egy rövid programrészleten fogjuk bemutatni.

10.1. Matematikai kifejezések kiértékelése
Az els problémakört a különböz el jel számokat tartalmazó matematikai kifejezések o o o u kiértékelése alkotja. Például tegyük fel, hogy AL-ben van egy el jeles, míg BX-ben egy el o o jel nélküli érték, és mi ezt a két számot szeretnénk összeadni, majd az eredményt a DX:AX regiszterpárban tárolni. Az ehhez hasonló feladatoknál mindig az a megoldás, hogy a két összeadandót azonos méret re kell hozni. Ez egész pontosan két dolgot jelent: az el jeles számokat u o el jelesen, az el jelteleneket zéró-kiterjesztésnek (el jeltelen-kiterjesztésnek) kell alávetni. Iso o o métlésként: zéró-kiterjesztésen (zero extension) azt értjük, amikor az adott érték fels , hiányzó o bitjeit csupa 0-val töltjük fel, ellentétben az el jeles kiterjesztéssel, ahol az el jelbitet használo o juk kitölt értékként. Azt is vegyük figyelembe, hogy az eredmény (összeg) mindig hosszabb o lesz 1 bittel mint a kiinduló tagok hosszának maximuma. Ha ez megvan, akkor jöhet a tényleges összeadás. Itt figyelnünk kell arra, mit és milyen sorrendben adunk össze. El ször az alsó bájo tokon/szavakon végezzük el a m veletet. Itt kapunk egy részeredményt, valamint egy esetleges u átvitelt, amit a fels bájtok/szavak összeadásakor is figyelembe kell venni. o Ezek alapján nézzünk egy lehetséges megoldást:
CBW CWD ADD ADC

AX,BX DX,0000h

El ször tisztázzuk az eredmény méretét. Az el jeles szám 8 bites, az el jeltelen 16 bites, ebb l o o o o 17 bit jön ki. Az alsó 16 bit meghatározása nem nagy kunszt, a CBW utasítás szépen kiterjeszti el jelesen AL-t AX-be, amihez aztán hozzáadhatjuk BX tartalmát. Az eredmény alsó 16 bitje o ezzel már megvan. A legfels bit azonban, mint gondolhatnánk, nem maga az átvitel lesz. o

10.1. MATEMATIKAI KIFEJEZÉSEK KIÉRTÉKELÉSE

57

Lássuk mondjuk, mi lesz, ha AL = -1, BX = 65535. AX-ben el jeles kiterjesztés után 0FFFFh o lesz, de ugyanezt fogja BX is tartalmazni. A két számot összeadva 0FFFEh-t kapunk, és CF = 1 lesz. Látható, hogy a helyes eredmény is 0 FFFEh lesz, nem pedig 1 FFFEh. A megoldás kulcsa, hogy az eredményt 24 vagy 32 bitesnek tekintjük. Most az utóbbit választjuk, hiszen DX:AXben várjuk az összeget. Innen már következik a módszer: mindkét kiinduló számot 32 bitesnek képzeljük el, s az összeadást is eszerint végezzük el. Az AX-ben lev el jeles számot az CWD o o (Convert Word to Doubleword) operandus nélküli utasítás DX:AX-be el jelesen kiterjeszti, az o összes flag-et békén hagyva. A másik, BX-ben lev tagnak zéró-kiterjesztésen kellene átesnie, o amit mi most kihagyunk, de az összeadás során figyelembe fogunk venni. Miután AX-ben képeztük az eredmény alsó szavát, itt az ideje, hogy a fels szavakat is összeadjuk az átvitellel o együtt. A kétoperandusú ADC (ADd with Carry) utasítás annyiban tér el az ADD-t l, hogy a o célhoz CF zéró-kiterjesztett értékét is hozzáadja. Az el jeles tag fels szava már DX-ben van, a o o másik tag fels szava azonban 0000h. Ezért az utolsó utasítással DX-hez hozzáadjuk a közvetlen o adatként szerepl nullát és CF-et is. Ha mindenképpen zéró kiterjesztést akarunk, akkor így kell o módosítani a programot:
CBW CWD XOR ADD ADC

CX,CX AX,BX DX,CX

CX helyett persze használhatunk más regisztert is a nulla tárolására. A példát befejezve, DX 0FFFFh-t fog tartalmazni a CWD hatására, amihez a 0000h-t és az átvitelt hozzáadva DX is nullává válik. DX:AX így a helyes eredményt fogja tartalmazni, ami 0000 FFFEh. A feladatban helyettesítsük most az összeadást kivonással, tehát szeretnénk az AL-ben lev o el jeles számból kivonni a BX-ben lev el jel nélküli számot, az eredményt ugyancsak DX:AXo o o ben várjuk. A megoldás a következ lehet: o
CBW CWD SUB SBB

AX,BX DX,0000h

Teljesen világos, hogy az összeadásokat a programban is kivonásra kicserélve célhoz érünk. Az ADC párja az SBB (SuBtract with Borrow), ami a SUB-tól csak annyiban tér el, hogy a célból CF zéró-kiterjesztett értékét is kivonja. Mind a 4 additív utasítás az összes aritmetikai flag-et, tehát a CF, PF, AF, ZF, SF és OF flageket módosítja. Operandusként általános regiszteren és memóriahivatkozáson kívül konstans értéket is kaphatnak. Térjünk most rá a szorzásra és osztásra. A gépi aritmetika tárgyalásánál már említettük, hogy szorozni és osztani sokkal körülményesebb, mint összeadni és kivonni. A gondot az el o jeles és el jeltelen számok csak tovább bonyolítják. Azt is említettük, hogy el jeles esetben a o o tagok el jelét le kell választani a m veletek elvégzéséhez, miután megállapítottuk az eredmény o u el jelét. (Ez persze nem a mi dolgunk, a processzor elvégzi helyettünk.) Ezen okból mind o szorzásból mind osztásból létezik el jeles és el jel nélküli változat is. Mindegyik utasításban o o közös, hogy az egyik forrás tag és az eredmény helye is rögzítve van, továbbá mindegyik uta-

10.1. MATEMATIKAI KIFEJEZÉSEK KIÉRTÉKELÉSE

58

sítás egyetlen operandust kap, ami csak egy általános regiszter vagy memóriahivatkozás lehet. Közvetlen értékkel tehát nem szorozhatunk, de nem is oszthatunk! Nézzük meg el ször az el jel nélküli változatokat, ezek az egyszer bbek. o o u Szorozni a MUL (unsigned MULtiplication) utasítással tudunk. Ennek egyetlen operandusa az egyik szorzó tagot (multiplier) tartalmazza. Az operandus mérete határozza meg a továbbiakat: ha 8 bites az operandus, akkor AL-t szorozza meg azzal, az eredmény pedig AX-be kerül. Ha szó méret volt az operandus, akkor másik tagként AX-et használja, az eredmény pedig u DX:AX-ben keletkezik. Osztásra a DIV (unsigned DIVision) utasítás szolgál, ezzel már korábban találkoztunk, de azért felelevenítjük használatát. Az egyetlen operandus jelöli ki az osztót (divisor). Ha ez bájt méret , akkor AX lesz az osztandó (dividend), és a hányados (quotient) AL-be, a maradék u (remainder) pedig AH-ba fog kerülni. Ha az operandus szavas volt, akkor DX:AX-et osztja el vele, majd a hányados AX-be, a maradék pedig DX-be kerül. Az el jeles esetben mindkét utasítás ugyanazokat a regisztereket használja a forrás illetve a o cél tárolására, mint az el jel nélküli változatok. o El jeles szorzást az IMUL (Integer signed MULtiplication) utasítással hajthatunk végre. Az o eredmény el jele a szokásos szabály szerint lesz meghatározva, tehát a két forrás el jelbitjét o o logikai KIZÁRÓ VAGY kapcsolatba hozva kapjuk meg. Végül el jelesen osztani az IDIV (Integer signed DIVision) utasítás alkalmazásával tudunk. o A hányados el jele ugyanúgy lesz meghatározva, mint az IMUL esetén, míg a maradék az oszo tandó el jelét örökli. o Ezek az utasítások elég ,,rendetlenül" módosítják a flag-eket: a szorzások a CF és OF flag-et változtatják meg, míg a PF, AF, ZF és SF flag-ek értéke meghatározatlan, az osztó utasítások viszont az összes el bb említett flag-et definiálatlan állapotban hagyják (azaz nem lehet tudni, o megváltozik-e egy adott flag értéke, s ha igen, mire és miért). Ha a szorzat nagyobb lenne, mint a források mérete, akkor a MUL és IMUL utasítások mind CF-et, mind OF-et 1-re állítják. Különben mindkét flag törl dni fog. Ez egész pontosan azt o jelenti, hogy ha bájtos esetben AH, szavas esetben pedig DX értékes biteket tartalmaz a szorzás elvégzése után, akkor állítják be a két említett flag-et a szorzó utasítások. Az utasítások használatának szemléltetésére nézzünk meg egy kicsit összetett példát! Tegyük fel, hogy a következ kifejezés értékét akarjuk meghatározni: o A· B+C ·D (E - F) /G A bet k hét számot jelölnek, ezek közül A és B el jel nélküli bájtok, C és D el jeles bájtok, E és u o o F el jel nélküli szavak, és végül G el jel nélküli bájt. Feltesszük, hogy E nagyobb vagy egyenl o o o F-nél. Az eredmény egy el jeles szó lesz, amit m veletek elvégzése után AX-ben kapunk meg. o u Az osztásoknál a maradékkal nem tör dünk, így az eredmény csak közelít pontosságú lesz. o o Hasonlóan nem foglalkozunk az esetleges túlcsordulásokkal sem. Lássuk hát a megoldást:
MOV SUB DIV MOV XOR MOV MUL MOV AX,[E] AX,[F] [G] CL,AL CH,CH AL,[A] [B] BX,AX

10.1. MATEMATIKAI KIFEJEZÉSEK KIÉRTÉKELÉSE

59

MOV IMUL CWD ADD ADC IDIV

AL,[C] [D] AX,BX DX,0000h CX

Érdemes átgondolni, hogy a 6 m veletet milyen sorrendben célszer elvégezni. El ször most u u o az E-F kivonást hajtjuk végre, amit rögvest elosztunk G-vel, az eredményt berakjuk CL-be, s ezt rögtön zéró-kiterjesztésnek vetjük alá, azaz CH-t kinullázzuk. Ezután, hogy minél kevesebb regiszter-m velet legyen, az el jeltelen szorzást végezzük el el bb, az eredményt BX-ben tárolu o o juk. Most jön a másik, el jeles szorzat kiszámolása, aminek az eredményét rögtön el jelesen o o kiterjesztjük DX:AX-be, hogy az összeadást végre tudjuk hajtani. Az összeg meghatározása után elvégezzük a nagy el jeles osztást, s ezzel AX-ben kialakul a végleges eredmény. o A teljes korrektség kedvéért megjegyezzük, hogy ez a példa két helyen ,,sántít". Nevezetesen az osztásoknál nem biztos, hogy a hányados el fog férni a számára kijelölt helyen, és ez bizony súlyos hibához vezethet (a kíváncsiaknak eláruljuk, hogy ilyen esetben egy osztási kivétel keletkezik). Ennek ellen rzését l, kikerülését l azonban most eltekintünk. o o o Utolsó esetként megnézzük, hogyan képezhetjük egy szám additív inverzét, vagy magyarosabban a -1-szeresét. (Szintén helyes, ha azt mondjuk, hogy képezzük a szám kettes komplemensét.) Erre külön utasítás szolgál. Az egyoperandusú NEG (NEGate) utasítás az operandusát kivonja 0-ból, az eredményt pedig visszaírja az operandusba. Operandusként általános regisztert vagy memóriahivatkozást adhatunk meg. Nem véletlen, hogy ,,kivonást" írtunk, ugyanis ha az operandus egyenl 0-val, akkor CF törl dni fog. Ha az operandus nemzéró, CF értéke minden o o esetben 1 lesz. Az utasítás különben mind a 6 aritmetikai flag-et az eredménynek megfelel en o megváltoztatja. Els példaként tegyük fel, hogy a AX-ben lev értéket szeretnénk kivonni a 0-ból. A mego o oldás igen egyszer : u
NEG AX

Kicsit bonyolítsunk a helyzeten. Most a cél a DX:AX regiszterpárban lev szám inverzének o meghatározása legyen. Els gondolatunk a következ : o o
NEG NEG AX DX

Ez azonban rossz megoldás! Miért? Próbáljuk ki mondjuk a -32768-ra. Ennek a számnak a 0FFFF 8000h felel meg, tehát DX = 0FFFFh és AX = 8000h. Tudjuk, hogy az eredménynek +32768 = 0000 8000h-nak kell lennie. Ha a fenti két utasítást elvégezzük, rossz eredményként 0001 8000h-t kapunk. A hiba abban van, hogy a kiinduló szám 32 bites, amit mi két különálló 16 bites számként kezeltünk. Ha visszaemlékszünk arra, hogyan is definiáltuk a kettes komplemens fogalmát, rálelhetünk egy jó megoldásra:
NOT NOT AX DX

10.2. BCD ARITMETIKA

60
ADD ADC AX,0001h DX,0000h

El ször tehát képezzük a kiinduló érték egyes komplemensét, majd ezt megnöveljük eggyel. Ez o egész biztosan helyes eredményt szolgáltat. Egy másik megoldást is bemutatunk:
NEG ADC NEG AX DX,0000h DX

Ha nem világos, miért is m ködik ez, akkor gondolkozzunk el rajta, hogyan végezzük el a u kivonást binárisan. A feladatban a kiinduló számot (DX:AX) ki kell vonni 0-ból. Ezt csak két lépésben tehetjük meg, de arra vigyázni kell, hogy a szám alsó és fels szava közti kapcsolatot o fenntartsuk. Ez jelen esetben annyit jelent, hogy az alsó szó kivonása (azaz NEG AX elvégzése) után az esetleges átvitelt le kell vonnunk a fels szóból. Mi most hozzáadtunk, de ez így helyes, o hiszen - (DX + CF) = -DX - CF, tehát mégiscsak levontuk az átvitelt.

10.2. BCD aritmetika
Mivel a hétköznapokban általában a decimális (10-es alapú) számrendszert használjuk, kényelmesebb lenne, ha a számítógépet is rá tudnánk venni, hogy ezt alkalmazza a bináris helyett. Nos, a BCD (Binary Coded Decimal) aritmetika pont ezt támogatja. A kifejezés jelentése egyértelm : binárisan kódolt decimális. Azaz arról van szó, hogy bár a processzor bináris regisztereit u és utasításait használjuk, de az adatokat (számokat) decimális alakban adjuk meg. Ennek használatához két új adattípus áll rendelkezésre. Lehet ségünk van 8 biten egy vagy o két decimális jegy tárolására is. Ennek alapján beszélhetünk pakolt (csomagolt) vagy pakolatlan (csomagolatlan) BCD számokról. Pakolt esetben két decimális jegyet tárolunk egyetlen bájtban, a bájt alsó felén a kisebb helyiérték , fels felén (4 bitjén) pedig a magasabb helyiéru o ték jegyet. Decimális jegy alatt egy 0h ­ 9h közötti értéket értünk, de ez nem azonos a ,,0" ­ ,,9" u karakterekkel! (Ez azért van, mert az ASCII kódtáblázat szerint a ,,0" karakter kódja 48d, avagy 30h.) Pakolatlan esetben a bájt fels 4 bitjének nincs szerepe, ott nem tárolunk semmit sem. o Fontos, hogy csak nemnegatív értékeket tárolhatunk ilyen módon! A tényleges utasítások ismertetése el tt kezdjük egy példával. Vegyünk két számot, ezek o legyenek mondjuk a 28 és a 13. Pakolatlan alakban tároljuk el oket mondjuk az AX és BX regiszterekben. Ezt annyit jelent az el z ek alapján, hogy AX = 0208h és BX = 0103h. Adjuk o o össze a két számot az ADD AH,BH // ADD AL,BL utasításokkal! Ennek hatására AX tartalma 030Bh lesz a bináris összeadás szabályai miatt. A helyes eredmény a 41 lenne, de nem ezt kaptuk. Nem véletlenül, hiszen az összeg alsó bájtja érvénytelen, mivel nem decimális jegyet tartalmaz. Ennek korrigálását végzi el az AAA (ASCII Adjust after Addition) operandus nélküli utasítás. M ködése röviden: ha volt decimális átvitel (azaz AF = 1) vagy AL alsó bitnégyese éru vénytelen (azaz (AL AND 0Fh) > 9), akkor AL-hez hozzáad 6-ot, megnöveli AH-t, majd AF-be és CF-be 1-et tölt. Különben a két flag értéke 0 lesz. A végén törli AL fels 4 bitjét. Esetünkben o AL alsó fele érvénytelen, ezért hozzáad ahhoz 6-ot (0311h), megnöveli AH-t (0411h), kitörli AL fels felét (0401h), AF-et és CF-et pedig 1-re állítja. Közben AX-ban kialakul az immár helyes o végeredmény.

10.2. BCD ARITMETIKA

61

Ismét vegyünk két számot, tekintsük mondjuk a 45-öt és a 17-et. Szintén pakolatlan alakban tároljuk el oket AX-ben és BX-ben. Most tehát AX = 0405h, BX = 0107h. Vonjuk ki a kisebbet a nagyobból a SUB AH,BH // SUB AL,BL utasítással! Hatására AX tartalma 03FEh lesz, ami igen furcsa eredmény. Ezt a szintén operandus nélküli AAS (ASCII Adjust after Subtraction) utasítással hozhatjuk helyre. M ködése: ha AL alsó négy bitje érvénytelen jegyet tartalmaz, u vagy AF = 1, akkor AL-b l kivon 6-ot, csökkenti AH-t, AF-et és CF-et pedig 1-re állítja. Különo ben a két flag tartalma 0 lesz. A végén törli AL fels 4 bitjét. A példánknál maradva, itt mindkét o feltétel teljesül, ezért kivon AL-b l 6-ot (03F8h), csökkenti AH-t (02F8h), törli AL fels felét o o (0208h), AF-et és CF-et pedig 1-re állítja. AX-ban így kialakul a helyes eredmény. Kérdezhetné valaki, hogy miért nem a SUB AX,BX utasítással végeztük el a kivonást. A válasz egyszer : azért, mert akkor eredményül 02FEh-t kaptunk volna, amit az AAS 0108h-ra u alakítana, és mi nem ezt az eredményt várjuk. A kulcs itt az átvitelben van, amit mi most nem akarunk a szám fels bájtjába átvinni. Másrészt pedig az alsó jegyeket kell kés bb kivonni, o o mivel az AAS m ködése az AF értékét l is függ. Ha a fels jegyeket vonnánk ki kés bb, akkor u o o o AF tartalma ez utóbbi kivonás szerint állna be, ami helytelenné tenné a végs eredményt. Ezt o szemléltetend hajtsuk végre a SUB AL,BL // SUB AH,BH utasításokat az AX = 0200h, BX = o 0009h értékekre! Hatásukra AX = 02F7h és AF = 0 lesz. Ha erre hajtjuk végre az AAS utasítást, akkor annyit változik a helyzet, hogy AX fels 4 bitje törl dik, 0207h-t eredményezve. Ha a két o o utasítást fordított sorrendben végezzük el, akkor AX változatlanul 02F7h-t fog tartalmazni, de most AF értéke 1 lesz. Ekkor alkalmazva az AAS-t, az korrigálni fogja AX-et, a helyes 0101h eredményt el állítva benne. o Most legyen AL = 04h, BL = 08h. Szorozzuk össze a két számot a MUL BL utasítással! Ennek eredményeként AX tartalma 0020h lesz, ami a 32 bináris alakja. Ezt az értéket szeretnénk pakolatlan BCD alakra hozni. Az operandus nélküli AAM (ASCII Adjust after Multiplication) utasítás AL tartalmát maradékosan elosztja 10-zel, a hányadost AH-ba, a maradékot pedig ALbe téve. (Vigyázat, ez nem elírás, itt az osztásoktól eltér en tényleg fordítva helyezkedik el a o hányados és a maradék!) A példánkra alkalmazva AX tartalma 0302h lesz. Most osztani fogunk. Legyen AX = 0205h, BL = 05h. El akarjuk osztani az els számot o a másodikkal. Osztani csak bináris alakban tudunk, ehhez viszont mindkét tagot erre az alakra kell hozni. Az 5-tel nincs is gond, a 25-öt viszont át kell alakítani. Erre kényelmes megoldás az ismét csak operandus nélküli AAD (ASCII Adjust before Division) utasítás. Hatására AL tartalma az AH · 10 + AL kifejezés értéke lesz, AH pedig kinullázódik. Az AAD után már elvégezhetjük az osztást a DIV BL utasítással. Ennek eredményeként AX tartalma 0005h lesz, ez a helyes eredmény. A szorzásra és osztásra itt most nagyon egyszer példákat írtunk csak. A bonyolultabb u esetekkel nem foglalkozunk, ezt az Olvasóra hagyjuk. Nagyszer lehet ség a gondolkodásra és u o gyakorlásra egyaránt. Az AAA, AAS és AAM utasításokat mindig az adott m velet utáni, az AAD utasítást pedig u a m velet el tti korrekcióra használjuk! Az AAA és AAS csak a CF és AF flag-eket változtatja u o ,,logikusan", a maradék 4 aritmetikai flag értéke meghatározatlan. Ezzel szemben az AAM és AAD utasítások PF, SF és ZF tartalmát változtatják definiáltan, a maradék 3 aritmetikai flag értéke itt is definiálatlan lesz a végrehajtás után. Eddig a pakolatlan BCD számok használatát mutattuk be. Most lássuk, mit m velhetünk a u pakolt BCD alakkal! Ismét tekintsük els példánkat, azaz a 28-as és 13-as számokat! Tároljuk o oket AL-ben és BL-ben, tehát legyen AL = 28h, BL = 13h. Az ADD AL,BL utasítással elvégezve az összeadást AL tartalma 3Bh lesz, ami persze nem helyes eredmény. Ezt most az operandus nélküli DAA (Decimal Adjust after Addition) utasítással hozhatjuk helyre. Ennek m ködése u

10.3. BITFORGATÓ UTASÍTÁSOK

62

már kicsit bonyolultabb: ha AL alsó fele érvénytelen jegyet tartalmaz, vagy AF = 1, akkor ALhez hozzáad 6-ot, AF-be pedig 1-et tölt. Különben AF értéke 0 lesz. Ezután ha AL fels 4 bitje o érvénytelen jegyet tartalmaz, vagy CF = 1, akkor AL-hez hozzáad 60h-t, és CF-et 1-re állítja. Különben CF értéke 0 lesz. Nem nehéz, egyszer en szükség szerint korrigálja mindkét jegyet. u Fontos, hogy m ködése közben az ,,összeadásokat" úgy végzi el, hogy a flag-ek tartalma nem u módosul. Példánkhoz visszatérve, itt most CF = AF = 0, és az alsó jegy érvénytelen. Ezért a DAA hozzáad AL-hez 6-ot (41h), AF-et 1-re állítja, és kész. A kivonáshoz tekintsük a második példát, tehát a 45-ös és 17-es számokat. Ehhez legyen AL = 45h, BL = 17h. Vonjuk ki a kisebbet a nagyobból a SUB AL,BL utasítással. Eredményként AL tartalma 2Eh lesz. Ezt ismét korrigálni kell az operandus nélküli DAS (Decimal Adjust after Subtraction) utasítás segítségével. M ködése teljesen megfelel a DAA m ködésének, annyi u u eltéréssel, hogy 6-ot illetve 60h-t von ki az alsó és a fels jegyb l szükség esetén. A példában o o CF = 0 és AF = 1, így a DAS kivon AL-b l 6-ot (28h), AF és CF értéke változatlan marad. o Mindkét utasítás módosítja a CF, PF, AF, ZF és SF flag-eket, OF tartalma meghatározatlan lesz végrehajtásuk után. Szorzás és osztás esetére nincs ilyen korrigáló utasítása a processzornak, így ezekkel a m u veletekkel nehezebb dolgunk lesz.

10.3. Bitforgató utasítások
Következ témánk a bitforgató utasításokat tekinti át. Ezek alapvet en két csoportra oszto o hatók: shiftel és rotáló utasításokra. Az összes ilyen utasítás közös jellemz je, hogy célopeo o randusuk általános regiszter vagy memóriahivatkozás lehet, míg a második operandus a léptetés/forgatás számát adja meg bitekben. Ez az operandus vagy a közvetlen 1-es érték, vagy a CL regiszter lehet. (A TASM megenged 1-nél nagyobb közvetlen értéket is, ekkor a megfelel o utasítás annyiszor lesz lekódolva. Így pl. az SHL AX,3 hatására 3 db. SHL AX,1 utasítást fog az assembler generálni.) CL-nek minden bitjét figyelembe veszi a 8086-os processzor, így pl. akár 200-szor is léptethetünk. A legutoljára kicsorgó bit értékét minden esetben CF tartalmazza. A shiftelés fogalmát már definiáltuk a gépi aritmetika elemzése közben (3. fejezet), ezért itt csak annyit említünk meg, hogy shiftelésb l megkülönböztetünk el jeles (ez az ú.n. arito o metikai) és el jeltelen (ez a logikai) változatot, és mindkett b l van balra és jobbra irányuló o o o utasítás is. Mindegyik shiftel utasítás módosítja a CF, PF, SF, ZF és OF flag-eket, AF értéke o meghatározatlan lesz. Ha a lépésszám 1-nél nagyobb, akkor OF értéke is meghatározatlan lesz. Balra shiftelni az SHL (SHift logical Left) és az SAL (Shift Arithmetical Left) utasításokkal tudunk, közülük a második szolgál az el jeles léptetésre. Balra shiftelésnél azonban mindegy, o hogy a céloperandus el jeles vagy el jeltelen érték-e, ezért, bár két mnemonik létezik, tényleo o gesen csak 1 utasítás van a gépi kód szintjén. Ezért ne csodálkozzunk, ha mondjuk a Turbo Debugger nem ismeri az SAL mnemonikot (de például a JC-t sem ismeri . . . ). Jobbra shiftelésnél már valóban meg kell különböztetni az el jeles változatot az el jel nélo o külit l. Az SHR (SHift logical Right) el jel nélküli, míg az SAR (Shift Arithmetical Right) o o el jeles léptetést hajt végre a céloperanduson. o Nézzünk most egy egyszer példát a shiftelés használatára. Tegyük fel, hogy AL-ben egy u el jeles bájt van, amit szeretnénk megszorozni 6-tal, az eredményt pedig AX-ben várjuk. Ezt o igazán sokféle módszerrel meg lehet oldani (LEA, IMUL, ADD, SUB stb.), de mi most léptetések segítségével tesszük meg. A megoldás majdnem triviális: a 6-tal való szorzás ugyanazt jelenti, mintha az eredeti szám dupláját és négyszeresét adnánk össze.

10.3. BITFORGATÓ UTASÍTÁSOK

63
CBW SHL MOV SHL ADD

AX,1 BX,AX AX,1 AX,BX

Ennél szebb megoldás is lehetséges, de a lényeg ezen is látszik. A program m ködése remélheu t leg mindenkinek világos. o Hasonló elgondolással megoldható az is, ha mondjuk az el jeles AL tartalmát meg akarjuk o szorozni 3/2-del, az eredményt itt is AX-ben várva.
CBW MOV SAR ADD

BX,AX AX,1 AX,BX

Rotáláson (forgatáson) azt a, léptetéshez igen hasonló m veletet értjük, amikor az operanu dus bitjei szépen egymás után balra vagy jobbra lépnek, miközben a kicsorgó bit a túloldalon visszalép, ,,körbefordul". Ez a fajta rotálás az operandus értékét ,,meg rzi", legalábbis olyan o értelemben, hogy ha mondjuk egy bájtot 8-szor balra vagy jobbra rotálunk, az eredeti változatlan bájtot kapjuk vissza. Rotálni nyilván csak el jeltelen értékeket lehet (tehát az el jelbitnek o o itt nincs speciális szerepe). Balra a ROL (ROtate Left), jobbra pedig a ROR (ROtate Right) mnemonikokkal lehet forgatni. Bármelyik forgató utasítás csak a CF és OF értékét módosítja. Ha 1-nél többször forgatunk CL-t használva, akkor OF tartalma meghatározatlan. A forgatásnak van egy másik változata is, ami egy igen hasznos tulajdonsággal bír. A forgatást ugyanis úgy is el lehet végezni, hogy nem a kilép bit fog belépni, hanem CF forgatás o el tti értéke. Szemléletesen ez annyit jelent, mintha a CF bit az operandus egy plusz bitje lenne: o balra forgatás esetén a legfels utáni, míg jobbra forgatáskor a legalsó bit el tti bit szerepét tölti o o be. Ekkor azt mondjuk, hogy carry-n (CF-en) keresztül forgatunk. Ilyen módon balra az RCL (Rotate through Carry Left), jobbra az RCR (Rotate through Carry Right) utasítás forgat. Forgatást például olyankor használunk, ha mondjuk szeretnénk az operandus minden egyes bitjét egyenként végigvizsgálni. A következ program például a BL-ben lev érték bináris alako o ját írja ki a képerny re. o
MOV MOV @Ciklus: MOV ROL ADC INT LOOP DL,'0' BL,1 DL,00h 21h @Ciklus AH,02h CX,8

Az algoritmus m ködése azon alapul, hogy a ROL a kiforgó bitet CF-be is betölti, amit azután u szépen hozzáadunk a ,,0" karakter kódjához. A forgatást nyolcszor elvégezve BL-ben újra a kiinduló érték lesz, s közben már a kiírás is helyesen megtörtént. Szintén jó szolgálatot tehet valamelyik normál rotáló utasítás, ha egy regiszter alsó és fels o bájtját, vagy alsó és fels bitnégyesét akarjuk felcserélni. Például az o

10.4. BITMANIPULÁLÓ UTASÍTÁSOK

64
ROL ROR AL,4 SI,8

utasítások hatására AL alsó és fels fele megcserél dik, majd SI alsó és fels bájtjával történik o o o meg ugyanez. Ha saját magunk akarunk megvalósítani nagy pontosságú aritmetikát, akkor is sokszor nyúlunk a shiftel és rotáló utasításokhoz. Tegyük fel mondjuk, hogy a shiftelést ki akarjuk terjeszo teni duplaszavakra is. A következ két sor a DX:AX-ben lev számot lépteti egyszer balra: o o
SHL RCL AX,1 DX,1

Az alapmódszer a következ : balra léptetésnél az els utasítás az SHL vagy SAL legyen, a többi o o pedig az RCL, és a legalsó bájttól haladunk a legfels , legértékesebb bájt felé. Jobbra léptetés o esetén a helyzet a fordítottjára változik: a legfels bájttól haladunk lefelé, az els utasítás a o o szám típusától függ en SHR vagy SAR legyen, a többi pedig RCR. Egyszerre csak 1 bittel o tudjuk ilyen módon léptetni a számot, de így is legfeljebb 7 léptetésre lesz szükség, mivel a LepesSzam db. bittel való léptetést megoldhatjuk, ha a számot LepesSzam mod 8-szor léptetjük, majd az egész területet LepesSzam/8 bájttal magasabb (avagy alacsonyabb) címre másoljuk. Utolsó példánkban az SI-ben lev számot el jelesen kiterjesztjük DI:SI-be. (A regiszterek o o ilyen ,,vad" választása természetesen szándékos.) Ezt megtehetnénk a hagyományos módon is, azaz a CWD utasítást használva, de ehhez SI-t AX-be kellene mozgatni, az eredményt pedig visszaírni a helyére. Így ráadásul AX és DX értéke is elromlana. Trükkösebb megoldás a következ : o
PUSH MOV MOV SAR POP CX DI,SI CL,15 DI,CL CX

A program m ködése abban rejlik, hogy az SAR utasítás a DI (és így végs soron SI) el jelbitu o o jével tölti fel DI-t, és az el jeles kiterjesztéshez pont ez kell. Igaz, így CL tartalma romlik el, o de nyugodtan el lehet hinni, hogy a 8086-osnál újabb processzorokon ez jobban megtérül. Akit érdekel, miért, eláruljuk, hogy a 80186-os processzortól kezdve 1-t l eltér közvetlen értékkel o o (azaz konstanssal) is léptethetünk, nemcsak CL-t használva.

10.4. Bitmanipuláló utasítások
Utolsó témakörünk a bitmanipulációk (beállítás, törlés, negálás és tesztelés) megvalósításával foglalkozik. Els ként a logikai utasítások ilyen célú felhasználását nézzük meg. Ebbe a csoportba 5 olyan o utasítás tartozik, amik az operandusok között ill. az operanduson valamilyen logikai m veletet u hajtanak végre bitenként. 3 utasítás (AND, OR és XOR) kétoperandusú, a m velet eredménye u minden esetben a céloperandusba kerül. A cél általános regiszter vagy memóriahivatkozás lehet, forrásként pedig ezeken kívül közvetlen értéket is írhatunk. A TEST szintén kétoperandusú, viszont az eredményt nem tárolja el. A NOT utasítás egyoperandusú, ez az operandus egyben

10.4. BITMANIPULÁLÓ UTASÍTÁSOK

65

forrás és cél is, típusa szerint általános regiszter vagy memóriahivatkozás lehet. Az utasítások elnevezése utal az általuk végrehajtott logikai m veletre is, ezért b vebben nem tárgyaljuk oket, u o a gépi logika leírásában megtalálható a szükséges információ. Az AND, OR, XOR és TEST utasítások a flag-eket egyformán kezelik: CF-et és OF-et 0-ra állítják, AF meghatározatlan lesz, PF, ZF és SF pedig módosulhatnak. A NOT utasítás nem változtat egyetlen flag-et sem. A TEST különleges logikai utasítás, mivel a két operandus között bitenkénti logikai ÉS m veletet végez, a flag-eket ennek megfelel en beállítja, viszont a két forrást nem bántja, és a u o m velet eredményét is eldobja. u Egyszer logikai azonosságok alkalmazása által ezekkel az utasításokkal képesek vagyunk u különféle bitmanipulációk elvégzésére. Az alapprobléma a következ : adott egy bájt, amiben o szeretnénk a 2-es bitet 0-ra állítani, a 4-es bitet 1-be billenteni, a 6-os bit értékét negálni, illetve kíváncsiak vagyunk, hogy az el jelbit (7-es bit) milyen állapotú. A bájtot tartalmazza most o mondjuk az AL regiszter. Minden kérdés egyetlen utasítással megoldható:
AND OR XOR TEST AL,0FBh AL,10h AL,40h AL,80h

A bit törléséhez a logikai ÉS m velet két jól ismert tulajdonságát használjuk fel: Bit AND 1 = u Bit, illetve Bit AND 0 = 0. Ezért ha AL-t olyan értékkel (bitmaszkkal) hozzuk logikai ÉS kapcsolatba, amiben a törlend bit(ek) helyén 0, a többi helyen pedig csupa 1-es áll, akkor készen o is vagyunk. Bitek beállításához a logikai VAGY m veletet és annak két tulajdonságát hívjuk segítségül: u Bit OR 0 = Bit, valamint Bit OR 1 = 1. AL-t ezért olyan maszkkal kell VAGY kapcsolatba hozni, amiben a beállítandó bit(ek) 1-es, a többiek pedig 0-ás értéket tartalmaznak. Ha egy bit állapotát kell negálni (más szóval invertálni vagy komplementálni), a logikai KIZÁRÓ VAGY m velet jöhet szóba. Ennek két tulajdonságát használjuk most ki: Bit XOR 0 = u Bit, és Bit XOR 1 = NOT Bit. A használt maszk ezek alapján ugyanúgy fog felépülni, mint az el bbi esetben. o Ha az összes bitet negálni akarjuk, azt megtehetjük az XOR operandus,11. . . 1b utasítással, de ennél sokkal egyszer bb a NOT operandus utasítás használata, ez ráadásul a flag-eket sem u piszkálja meg. (Az 11. . . 1b jelölés egy csupa 1-esekb l álló bináris számot jelent.) o Ha valamely bit vagy bitek állapotát kell megvizsgálnunk (más szóval tesztelnünk), a célravezet megoldás az lehet, hogy az adott operandust olyan maszkkal hozzuk ÉS kapcsolatba, o ami a tesztelend bitek helyén 1-es, a többi helyen 0-ás érték . Ha a kapott eredmény nulla (ezt o u ZF = 1 is jelezni fogja), akkor a kérdéses bitek biztosan nem voltak beállítva. Különben két eset lehetséges: ha egy bitet vizsgáltunk, akkor az a bit tutira be volt állítva, ha pedig több bitre voltunk kíváncsiak, akkor a bitek közül valamennyi (de legalább egy) darab 1-es érték volt az u operandusban. Ha ez utóbbi dologra vagyunk csak kíváncsiak (tehát a bitek értéke külön-külön nem érdekel bennünket, csak az a fontos, hogy legalább egy be legyen állítva), akkor felesleges az AND utasítást használni. A TEST nem rontja el egyik operandusát sem, és ráadásul a flag-eket az ÉS m velet eredményének megfelel en beállítja. u o A bitmanipuláció speciális esetét jelenti a Flags regiszter egyes bitjeinek, azaz a flag-eknek a beállítása, törlése stb. Az aritmetikai flag-eket (CF, PF, AF, ZF, SF, OF) elég sok utasítás képes megváltoztatni, ezek eredményét részben mi is irányíthatjuk megfelel operandusok váo lasztásával. A CF, DF és IF flag értékét külön-külön állíthatjuk bizonyos utasításokkal: törlésre

10.4. BITMANIPULÁLÓ UTASÍTÁSOK

66

a CLC, CLD és CLI (CLear Carry/Direction/Interrupt flag), míg beállításra az STC, STD és STI (SeT Carry/Direction/Interrupt flag) utasítások szolgálnak. Ezenkívül CF-et negálhatjuk a CMC (CoMplement Carry flag) utasítással. Mindegyik említett utasítás operandus nélküli, és más flag-ek értékét nem befolyásolják. Ha egy olyan flag értékét akarjuk beállítani, amit egyéb módon nehézkes lenne (pl. AF), vagy egy olyan flag értékére vagyunk kíváncsiak, amit eddig ismert módon nem tudunk írni/olvasni (pl. TF), akkor más megoldást kell találni. Két lehet ség is van: vagy a PUSHF-POPF, vagy a o LAHF-SAHF utasítások valamelyikét használjuk. Mindegyik utasítás operandus nélküli. A PUSHF (PUSH Flags) utasítás a Flags regiszter teljes tartalmát (mind a 16 bitet) letárolja a verembe, egyébként m ködése a szokásos PUSH m velettel egyezik meg. A POPF (POP u u Flags) ezzel szemben a verem tetején lev szót leemeli ugyanúgy, mint a POP, majd a megfelel o o biteken szerepl értékeket sorban betölti a flag-ekbe. (Ezt azért fogalmaztuk így, mivel a Flags o néhány bitje kihasználatlan, így azokat nem módosíthatjuk.) A másik két utasítás kicsit másképp dolgozik. A LAHF (Load AH from Flags) a Flags regiszter alsó bájtját az AH regiszterbe másolja, míg a SAHF (Store AH into Flags) AH 0, 2, 4, 6 és 7 számú bitjeit sorban a CF, PF, AF, ZF és SF flag-ekbe tölti be. Más flag-ek ill. a verem/veremmutató értékét nem módosítják. A szemléltetés kedvéért most AF-et mind a kétféle módon negáljuk: 1.
PUSHF POP XOR PUSH POPF

AX AL,10h AX

2.
LAHF XOR SAHF

AH,10h

11. fejezet

Az Assembly nyelv kapcsolata magas szintu nyelvekkel, a paraméterátadás formái
Ebben a fejezetben azt vizsgáljuk, hogy egy önálló Assembly program esetén egy adott eljárásnak/függvénynek milyen módon adhatunk át paramétereket, függvények esetében hogyan kaphatjuk meg a függvény értékét, illetve hogyan tudunk lokális változókat kezelni, azaz hogyan valósíthatjuk meg mindazt, ami a magas szint nyelvekben biztosítva van. Ezután megnézzük, u hogy a Pascal és a C pontosan hogyan végzi ezt el, de el bb tisztázzunk valamit: o Ha a meghívott szubrutinnak (alprogramnak) nincs visszatérési értéke, akkor eljárásnak (procedure), egyébként pedig függvénynek (function) nevezzük. Ezt csak emlékeztet ül mondo juk, no meg azért is, mert Assemblyben formálisan nem tudunk különbséget tenni eljárás és függvény között, mivel mindkett t a PROC-ENDP párral deklaráljuk, viszont ebben a deklao rációban nyoma sincs sem paramétereknek, sem visszatérési értéknek, ellentétben pl. a Pascallal (PROCEDURE, FUNCTION), vagy C-vel, ahol végülis minden szubrutin függvény, de ha visszatérési értéke VOID, akkor eljárásnak min sül, s a fordítók is ekként kezelik oket. o

11.1. Paraméterek átadása regisztereken keresztül
Ez azt jelenti, hogy az eljárásokat/függvényeket (a továbbiakban csak eljárásoknak nevezzük oket) úgy írjuk meg, hogy azok bizonyos regiszterekben kérik a paramétereket. Hogy pontosan mely regiszterekben, azt mi dönthetjük el tetsz legesen (persze ésszer keretek között, hiszen pl. o u a CS-t nem használjuk paraméterátadásra), de nem árt figyelembe vennünk azt sem, hogy hogy a legérdemesebb ezek megválasztása. A visszatérési értékeket (igen, mivel nem egy regiszter van, ezért több dolgot is visszaadhatunk) is ezeken keresztül adhatjuk vissza. Erre rögtön egy példa: van egy egyszer eljárásunk, amely egy bájtokból álló vektor elemeit összegzi. Bemeneti u paraméterei a vektor címe, hossza. Visszatérési értéke a vektor elemeinek el jeles összege. Az o eljárás feltételezi, hogy az összeg elfér 16 biten.
Pelda5.ASM:
Osszegez PROC

11.1. PARAMÉTEREK ÁTADÁSA REGISZTEREKEN KERESZTÜL

68

PUSH PUSHF CLD XOR @AddKov: LODSB CBW ADD LOOP POPF POP RET ENDP

AX ;SI-nek növekednie kell ;majd ;Kezdeti összeg = 0 ;Következo vektor;komponens AL-be ;elojelesen kiterjesztjük ;szóvá ;hozzáadjuk a meglevo ;összeghez ;Ismételd, amíg CX > 0

BX,BX

BX,AX @AddKov AX

Osszegez

· · ·
;Példa az eljárás ;meghívására ;13 bájt hosszú a vektor ;cím offszetrésze ;feltesszük,hogy DS az ;adatszegmensre mutat ;Pl. a DX-be töltjük az ;eredményt

MOV MOV

CX,13 SI,OFFSET Egyikvektor

CALL MOV

Osszegez DX,BX

· · ·
;valahol az ;adatszegmensben ;deklarálva vannak ;a változók: Hossz Cim Eredmeny Probavektor DW DD DW DB ? ? ? 13 DUP (?)

Ezt az eljárást úgy terveztük, hogy a vektor címét a DS:SI regiszterpárban, a vektor hosszát a CX regiszterben kapja meg, ugyanis az eljárás szempontjából ez a legel nyösebb (a LODSB o és LOOP utasításokat majdnem minden el készület után használhattuk). Az összeget az eljárás o a BX regiszterben adja vissza. Érdemes egy pillantást vetni a PUSHF-POPF párra: az irányjelz t töröljük, mert nem tudo hatjuk, hogy az eljárás hívásakor mire van állítva, de éppen e miatt fontos, hogy meg is orizzük azt. Ezért mentettük el a flageket.

11.2. PARAMÉTEREK ÁTADÁSA GLOBÁLISAN

69

Az AX regiszter is csak átmeneti ,,változó", jobb ha ennek értékét sem rontjuk el. A példában egy olyan vektor komponenseit összegeztük, amely globális változóként az adatszegmensben helyezkedik el.

11.2. Paraméterek átadása globális változókon keresztül
Ez a módszer analóg az el z vel, annyi a különbség, hogy ilyenkor a paramétereket bio o zonyos globális változókba teszünk, s az eljárás majd onnan olvassa ki oket. Magas szint u nyelvekben is megtehetjük ezt, azaz nem paraméterezzük fel az eljárást, de az felhasznál bizonyos globális változókat bemenetként (persze ez nem túl szép megoldás). Ennek mintájára a visszatérési értékeket is átadhatjuk globális változókban. Írjuk át az el z példát: o o
Pelda6.ASM:
Osszegez PROC PUSH PUSH PUSHF CLD XOR MOV LDS @AddKov: LODSB CBW ADD LOOP MOV POPF POP POP RET ENDP AX,BX @AddKov [Eredmeny],BX BX AX ;Következo vektor;komponens AL-be ;elojelesen kiterjesztjük ;szóvá ;hozzáadjuk a meglevo ;összeghez ;Ismételd, amíg CX > 0

AX BX ;SI-nek növekednie kell ;majd ;Kezdeti összeg = 0

BX,BX CX,[Hossz] SI,[Cim]

Osszegez

· · ·
;Példa az eljárás ;meghívására ;13 bájt hosszú a vektor ;ami az ;adatszegmensben ;van, így a cím ;szegmensrésze az DS ;cím offszetrésze ;feltesszük,hogy DS az

MOV MOV

[Hossz],13 WORD PTR Cim[2],DS

MOV

WORD PTR [Cim],OFFSET Egyikvektor

11.3. PARAMÉTEREK ÁTADÁSA A VERMEN KERESZTÜL

70
;adatszegmensre mutat ;

CALL MOV

Osszegez DX,[Eredmeny]

;Pl. a DX-be töltjük az ;eredményt

· · ·
;valahol az ;adatszegmensben ;deklarálva vannak ;a változók: Hossz Cim Eredmeny Egyikvektor Masikvektor DW DD DW DB DB ? ? ? 13 DUP (?) 34 DUP (?)

A példához nem kívánunk magyarázatot f zni, talán csak annyit, hogy mivel itt már a BX u is egy átmeneti változóvá lett (mert nem rajta keresztül adjuk vissza az összeget), ezért jobb, ha ennek értékét meg rzi az eljárás. o Van egy dolog, amire azonban nagyon vigyázni kell az effajta paraméterátadással. Képzeljük el, hogy már beírtuk a paramétereket a változókba, de még miel tt meghívnánk az eljárást o (a ,,"-gal jelzett helyen), bekövetkezik egy megszakítás. Tegyük fel, hogy a megszakításban egy saját megszakítási rutinunk hajtódik végre, ami szintén meghívja az Osszegez eljárást. Mi történik akkor, ha o a Masikvektor komponenseit összegezteti? O is beírja a paramétereit ugyanezekbe a globális változókba, meghívja az eljárást, stb., végetér a megszakítási rutin, és a program ugyanott folytatódik tovább, ahol megszakadt. Esetünkben ez azt jelenti, hogy meghívjuk az eljárást, de nem azokkal a paraméterekkel, amiket beállítottunk, mert a megszakítási rutin felülírta azokat a sajátjaival. Ez természetesen a program hibás m ködését okozza. Az u el z esetben, ahol regisztereken keresztül adtuk át a cuccokat, ott ez nem fordulhatott volna o o el , hiszen egy megszakításnak kötelessége elmenteni azokat a regisztereket, amelyeket felhaszo nál. A probléma megoldása tehát az, hogy ha megszakításban hívjuk ezt az eljárást, akkor el kell mentenünk, majd vissza kell állítanunk a globális változók értékeit, mintha azok regiszterek lennének.

11.3. Paraméterek átadása a vermen keresztül
Ennek lényege, hogy az eljárás meghívása el tt a verembe beletesszük az összes paramétert, o az eljárás pedig kiolvassa oket onnan, de nem a POP m velettel. Mivel a paraméterek sorrendje u rögzített, és a utánuk a verembe csak a visszatérési cím kerül, így azok az eljáráson belül az SP-hez relatívan elérhet ek. Ezek szerint olyan címzésmódot kellene használni, amelyben SP o közvetlenül szerepel, pl. MOV AX,[SP+6], csakhogy ilyen nem létezik. A másik probléma az SP-vel az lenne, hogy ha valamit ideiglenesen elmentünk a verembe, akkor a paraméterek relatív helye is megváltozik, így nehézkesebb lenne kezelni oket. Épp erre ,,találták ki" viszont a BP regisztert, emiatt van, hogy a vele használt címzésmódokban az alapértelmezett szegmens nem a DS, hanem az SS (de már a neve is: Base Pointer ­ Bázismutató, a vermen belül). A módszert ismét az el z példa átírásával mutatjuk be: o o

11.3. PARAMÉTEREK ÁTADÁSA A VERMEN KERESZTÜL

71

Pelda7.ASM:
Osszegez PROC PUSH MOV PUSH PUSHF CLD XOR MOV LDS @AddKov: LODSB CBW ADD LOOP POPF POP POP RET ENDP BX,AX @AddKov AX BP 6 ;Következo vektor;komponens AL-be ;elojelesen kiterjesztjük ;szóvá ;hozzáadjuk a meglevo ;összeghez ;Ismételd, amíg CX > 0

BP BP,SP AX ;SI-nek növekednie kell ;majd ;Kezdeti összeg = 0

BX,BX CX,[BP+8] SI,[BP+4]

Osszegez

· · ·
;Példa az eljárás ;meghívására MOV PUSH PUSH MOV PUSH CALL MOV AX,13 AX DS AX,OFFSET Egyikvektor AX Osszegez DX,BX ;13 bájt hosszú a vektor ;cím offszetrésze

;Pl. a DX-be töltjük az ;eredményt

· · ·
;valahol az ;adatszegmensben ;deklarálva vannak ;a változók: Hossz Cim Eredmeny DW DD DW ? ? ?

11.3. PARAMÉTEREK ÁTADÁSA A VERMEN KERESZTÜL

72

Probavektor

DB

13 DUP (?)

Az eljárásban BP értékét is meg riztük a biztonság kedvéért. A RET utasításban szerepl o o operandus azt mondja meg a processzornak, hogy olvassa ki a visszatérési címet, aztán az operandus értékét adja hozzá SP-hez. Ez azért kell, hogy az eljárás a paramétereit kitakarítsa a veremb l. Hogy jobban megértsük a folyamatot, ábrákkal illusztráljuk a verem alakulását: o A BP-t tehát ráállítjuk az utolsó paraméterre (pontosabban itt most BP elmentett értékére), ez a bázis, s ehhez képest +4 bájtra található a vektor teljes címe, amit persze úgy helyeztünk a verembe, hogy az alacsonyabb címen szerepeljen az offszetrész, aztán a szegmens. BP-hez képest pedig +8 bájtra található a vektor hossza. Az eljárásból való visszatérés után persze minden elt nik a veremb l, amit el tte belepakoltunk a hívásához. u o o A visszatérési értéket most BX-ben adja vissza, err l még lesz szó. o A magas szint nyelvek is vermen keresztüli paraméterátadást valósítanak meg. Ennek a u módszernek kétféle változata is létezik, aszerint, hogy a paraméterek törlése a veremb l kinek o a feladata: · Pascal-féle változat: a verem törlése az eljárás dolga, amint azt az el bb is láttuk o · C-féle változat: a verem törlése a hívó fél feladata, erre mindjárt nézünk példát A Pascal a paramétereket olyan sorrendben teszi be a verembe, amilyen sorrendben megad tuk oket, tehát a legutolsó paraméter kerül bele a verembe utoljára. A C ezzel szemben fordított sorrendben teszi ezt, így a legels paraméter kerül be utoljára. Ennek megvan az az el nye is, o o hogy könnyen megvalósítható a változó számú paraméterezés, hiszen ilyenkor az els paraméo ter relatív helye a bázishoz képest állandó, ugyanígy a másodiké is, stb., csak azt kell tudnia az eljárásnak, hogy hány paraméterrel hívták meg. Most pedig nézzük meg a példát, amikor a hívó törli a vermet:
Pelda8.ASM:
Osszegez PROC PUSH MOV PUSH PUSHF CLD XOR MOV LDS @AddKov: LODSB CBW ADD LOOP POPF POP POP BX,AX @AddKov AX BP ;Következo vektor;komponens AL-be ;elojelesen kiterjesztjük ;szóvá ;hozzáadjuk a meglevo ;összeghez ;Ismételd, amíg CX > 0

BP BP,SP AX ;SI-nek növekednie kell ;majd ;Kezdeti összeg = 0

BX,BX CX,[BP+8] SI,[BP+4]

11.4. LOKÁLIS VÁLTOZÓK MEGVALÓSÍTÁSA

73

Osszegez

RET ENDP

· · ·
;Példa az eljárás ;meghívására MOV PUSH PUSH MOV PUSH CALL ADD MOV AX,13 AX DS AX,OFFSET Egyikvektor AX Osszegez SP,6 DX,BX ;13 bájt hosszú a vektor ;cím szegmensrésze ;cím offszetrésze

;Pl. a DX-be töltjük az ;eredményt

Látható, hogy ilyenkor a visszatérési értéket is át lehetne adni a vermen keresztül úgy, hogy pl. az egyik paraméter helyére beírjuk azt, s a hívó fél onnan olvassa ki, miel tt törölné a vermet o (a Pascal-szer verzió esetén ez persze nem lehetséges). Ezt azonban nem szokás alkalmazni, a u C is mindig regiszterekben adja ezt vissza, ez a könnyebben kezelhet megoldás, mi is tegyünk o így.

11.4. Lokális változók megvalósítása
A lokális változókat kétféleképpen valósíthatjuk meg: az egyik, hogy elmentjük valamely regisztert, s felhasználjuk azt változóként, ugyanúgy, ahogy idáig is tettük az Osszegez eljárásban az AX-szel. A másik megoldás, amit a magas szint nyelvek is alkalmaznak, hogy maga u az eljárás kezdetben foglal a veremben a lokális változóknak helyet, ami annyit tesz, hogy SP értékét lecsökkenti valamennyivel. A változókat továbbra is a bázis-technikával (a BP-n keresztül) éri el. Írjuk át ismét az Osszegez-t úgy, hogy ezt a módszert alkalmazza (most a lokális változóban tároljuk ideiglenesen az összeget):
Pelda9.ASM:
Osszegez PROC PUSH MOV SUB PUSH PUSHF CLD MOV MOV LDS @AddKov:

BP BP,SP SP,2 AX

;Helyet foglalunk a ;lokális változónak

WORD PTR [BP-2],0h CX,[BP+8] SI,[BP+4]

;SI-nek növekednie kell ;majd ;Kezdeti összeg = 0

11.4. LOKÁLIS VÁLTOZÓK MEGVALÓSÍTÁSA

74
;Következo vektor;komponens AL-be ;elojelesen kiterjesztjük ;szóvá ;hozzáadjuk a meglevo ;összeghez ;Ismételd, amíg CX > 0

LODSB CBW ADD LOOP POPF POP MOV ADD POP RET ENDP [BP-2],AX @AddKov AX BX,[BP-2] SP,2 BP

;BX = lok. változó ;lok. vált. helyének ;felszabadítása

Osszegez

· · ·
;Példa az eljárás ;meghívására MOV PUSH PUSH MOV PUSH CALL ADD MOV AX,13 AX DS AX,OFFSET Egyikvektor AX Osszegez SP,6 DX,BX ;13 bájt hosszú a vektor ;cím szegmensrésze ;cím offszetrésze

;Pl. a DX-be töltjük az ;eredményt

Hogyan is néz ki a verem az eljárás futásakor?

12. fejezet

Muveletek sztringekkel
Sztringen bájtok vagy szavak véges hosszú folyamát értjük. A magas szint nyelvekben is u találkozhatunk ezzel az adattípussal, de ott általában van valamilyen megkötés, mint pl. a rögzített maximális hossz, kötött elhelyezkedés a memóriában stb. Assemblyben sztringen nem csak a szegmensekben definiált karakteres sztring-konstansokat értjük, hanem a memória tetsz leges o címén kezd d összefügg területet. Ez azt jelenti, hogy mondjuk egy 10 bájt méret változót o o o u tekinthetünk 10 elem tömbnek (vektornak), de kedvünk szerint akár sztringnek is; vagy például u a verem tartalmát is kezelhetjük sztringként. Mivel minden regiszter 16 bites, valamint a memória is szegmentált szervezés , ezeknek teru mészetes következménye, hogy a sztringek maximális hossza egy szegmensnyi, tehát 64 Kbájt. Persze ha ennél hosszabb folyamokra van szükség, akkor megfelel feldarabolással ez is mego oldható. A 8086-os mikroprocesszor 5 utasítást kínál a sztringekkel végzett munka megkönnyítésére. Ezek közül kett vel már találkoztunk eddig is. Az utasítások közös jellemz je, hogy a o o forrássztring címét a DS:SI, míg a cél címét az ES:DI regiszterpárból olvassák ki. Ezek közül a DS szegmenst felülbírálhatjuk, minden más viszont rögzített. Az utasítások csak bájtos vagy szavas elem sztringekkel tudnak dolgozni. Szintén közös tulajdonság, hogy a megfelel m veu o u let elvégzése után mindegyik utasítás a sztring(ek) következ elemére állítja SI-t és/vagy DI-t. o A következ elemet most kétféleképpen értelmezhetjük: lehet a megszokott, növekv irányú, o o illetve lehet fordított, csökken irányú is. A használni kívánt irányt a DF flag állása választja ki: o DF = 0 esetén növekv , míg DF = 1 esetén csökken lesz. o o Ugyancsak jellemz mindegyik utasításra, hogy az Assembly szintjén több mnemonikkal o és különféle operandusszámmal érhet k el. Pontosabban ez azt takarja, hogy mindegyiknek o létezik 2, operandus nélküli rövid változata, illetve egy olyan változat, ahol ,,áloperandusokat" adhatunk meg. Áloperandus (pseudo operand) alatt most egy olyan operandust értünk, ami csak szimbolikus, és nem adja meg a tényleges operandus címét/helyét, csak annak méretét, szegmensét jelöli ki. Ezekre a már említett megkötések (DS:SI és ES:DI) miatt van szükség. Az áloperandussal rendelkez mnemonikok a CMPS, LODS, MOVS, SCAS és STOS, míg az o egyszer alakok a CMPSB, CMPSW, LODSB, LODSW, MOVSB, MOVSW, SCASB, SCASW, u STOSB és STOSW. Minden rögtön világosabb lesz, ha nézünk rájuk egy példát:
Pelda10.ASM:
MODEL SMALL

76

.STACK ADAT Szoveg1 Szoveg2 Kozos Hossz Vektor ADAT KOD SEGMENT DB DB DB DW DW ENDS

"Ez az elso szöveg.",00h "Ez a második szöveg.",00h 10 DUP (?) ? 100 DUP (?)

SEGMENT ASSUME CS:KOD,DS:ADAT

@Start: MOV MOV MOV LEA LEA CLD XOR @Ciklus1: CMPSB JNE INC JMP @Vege: LEA LEA ADD DEC STD MOVS LEA MOV XOR CLD SCASB STC SBB MOV PUSH POP XOR LEA MOV LODSB CBW NOT STOSW SI,[SI-2] DI,[Kozos] DI,CX DI ES:[Kozos],DS:[SI] DI,[Szoveg1] CX,100 AL,AL @Vege CX @Ciklus1 AX,ADAT DS,AX ES,AX SI,[Szoveg1] DI,[Szoveg2] CX,CX

REP

REPNE

DI,OFFSET Szoveg1 [Hossz],DI CS DS SI,SI DI,[Vektor] CX,100

@Ciklus2:

AX

77
LOOP MOV INT ENDS END @Ciklus2 AX,4C00h 21h @Start

KOD

A program nem sok értelmeset csinál, de szemléltetésnek megteszi. El ször meg akarjuk tudni, hogy a Szoveg1 és a Szoveg2 00h kódú karakterrel terminált o sztringek elején hány darab megegyez karakter van. Ezt ,,hagyományos" módon úgy csinálo nánk, hogy egy ciklusban kiolvasnánk az egyik sztring következ karakterét, azt összehasonlío tanánk a másik sztring megfelel karakterével, majd az eredmény függvényében döntenénk a o továbbiakról. Számláláshoz CX-et használjuk. A ciklus most is marad csakúgy, mint a JNEJMP páros. Az összehasonlítást viszont másképp végezzük el. Els sztringkezel utasításunk mnemonikja igen hasonlít az egész aritmetikás összehasonlío o tásra. A CMPS (CoMPare String) mnemonik kétoperandusú utasítás. Mindkét operandus memóriahivatkozást kifejez áloperandus, és rendhagyó módon az els operandus a forrássztring, o o míg a második a célsztring. Az utasítás szintaxisa tehát a következ : o
CMPS forrássztring,célsztring

Az utasítás a forrássztringet összehasonlítja a célsztringgel, beállítja a flag-eket, de az operandusokat nem bántja. Egészen pontosan a célsztring egy elemét vonja ki a forrássztring egy eleméb l, és a flag-eket ennek megfelel en módosítja. A forrássztring alapértelmezésben a o o DS:SI, a célsztring pedig az ES:DI címen található, mint már említettük. Az els operandus o szegmensregisztere bármi lehet, a másodiké kötelez en ES. Ha egyik operandusból sem deo rül ki azok mérete (bájt vagy szó), akkor a méretet nekünk kell megadni a BYTE PTR vagy a WORD PTR kifejezés valamelyik operandus elé írásával. A PTR operátor az o jobb oldalán álló kifejezés típusát a bal oldalán álló típusra változtatja meg, ezt más nyelvekben hasonló módon típusfelülbírálásnak (type casting/overloading) hívják. Ha nem kívánunk az operandusok megadásával bajlódni, akkor használhatunk két másik rövidítést. A CMPSB és CMPSW (CoMPare String Byte/Word) operandus nélküli mnemonikok rendre bájt ill. szó elem sztringeket írnak u el a DS:SI és ES:DI címeken, tehát formálisan a CMPS BYTE/WORD PTR DS:[SI],ES:[DI] o utasításnak felelnek meg. A CMPSB utasítás tehát a sztringek következ karaktereit hasonlítja össze, majd SI-t és DI-t o is megnöveli 1-gyel, hiszen DF = 0 volt. Ha ZF = 0, akkor vége a ciklusnak, különben növeljük CX-et, és visszaugrunk a ciklus elejére. A @Vege címkére eljutva már mindenképpen megtaláltuk a közös rész hosszát, amit CX tartalmaz. SI és DI a két legutóbb összehasonlított karaktert követ bájtra mutat, így SI-t kett vel o o csökkentve az a közös rész utolsó karakterére fog mutatni. Ezt követ en a közös karakterláncot a Kozos változó területére fogjuk másolni. Mivel SI o most a sztring végére mutat, célszer , ha a másolást fordított irányban végezzük el. Ennek u megfelel en állítjuk be DI értékét is. DF-et 1-re állítva készen állunk a másolásra. o Következ új sztringkezel utasításunk a MOVS, MOVSB, MOVSW (MOVe String Byte/Word). o o Az utasítás teljes alakja a következ : o
MOVS cél,forrás

Az utasítás a forrás sztringet átmásolja a cél helyére, flag-et persze nem módosít. A példában

78 jól látszik, hogy a megadott operandusok tényleg nem valódi címre hivatkoznak: célként nem az ES:[Kozos] kifejezés tényleges címe lesz használva, hanem az ES:DI által mutatott érték, de forrásként is adhattunk volna meg bármit SI helyett. Ezek az operandusok csak a program dokumentálását, megértését segítik, bel lük az assembler csak a forrás szegmenst (DS) és a o sztring elemméretét (ami most a Kozos elemmérete, tehát bájt) használja fel, a többit figyelmen kívül hagyja. A MOVSB, MOVSW mnemonikok a CMPS-hez hasonlóan formálisan a MOVS BYTE/WORD PTR ES:[DI],DS:[SI] utasítást rövidítik. Ha egy adott sztring minden elemére akarunk egy konkrét sztringm veletet végrehajtani, u akkor nem kell azt feltétlenül ciklusba rejtenünk. A sztringutasítást ismétl prefixek minden o sztringkezel utasítás mnemonikja el tt megadhatók. Három fajtájuk van: REP; REPE, REPZ o o és REPNE, REPNZ (REPeat, REPeat while Equal/Zero/ZF = 1, REPeat while Not Equal/Not Zero/ZF = 0). A REP prefix m ködése a következ : u o 1. ha CX = 0000h, akkor kilépés, az utasítás végrehajtása befejez dik o 2. a prefixet követ sztringutasítás egyszeri végrehajtása o 3. CX csökkentése 1-gyel, a flag-ek nem változnak 4. menjünk az (1)-re Ha tehát kezdetben CX = 0000h, akkor nem történik egyetlen m velet sem, hatását tekintve u gyakorlatilag egy NOP-nak fog megfelelni az utasítás. Különben a sztringkezel utasítás 1-szer o végrehajtódik, CX csökken, és ez mindaddig folytatódik, amíg CX zérus nem lesz. Ez végül is azt eredményezi, hogy a megadott utasítás pontosan annyiszor kerül végrehajtásra, amennyit CX kezdetben tartalmazott. REP prefixet a LODS, MOVS és STOS utasításokkal használhatunk. A REPE, REPZ prefix a következ módon m ködik: o u 1. ha CX = 0000h, akkor kilépés, az utasítás végrehajtása befejez dik o 2. a prefixet követ sztringutasítás egyszeri végrehajtása o 3. CX csökkentése 1-gyel, a flag-ek nem változnak 4. ha ZF = 0, akkor kilépés, az utasítás végrehajtása befejez dik o 5. menjünk az (1)-re A REPE, REPZ tehát mindaddig ismétli a megadott utasítást, amíg CX 0000h és ZF = 1 is fennállnak. Ha valamelyik vagy mindkét feltétel hamis, az utasítás végrehajtása befejez dik. o A REPNE, REPNZ prefix hasonló módon akkor fejezi be az utasítás ismételt végrehajtását, ha a CX = 0000h, ZF = 1 feltételek közül legalább az egyik teljesül (tehát a fenti pszeudokódnak a (4) lépése változik meg). A REPE, REPZ és REPNE, REPNZ prefixeket a CMPS és a SCAS utasításoknál illik használni (hiszen ez a két utasítás állítja a flag-eket is), bár a másik három utasítás esetében m ködésük a REP-nek felel meg. u Még egyszer hangsúlyozzuk, hogy ezek a prefixek kizárólag egyetlen utasítás ismételt végrehajtására használhatóak, s az az utasítás csak egy sztringkezel mnemonik lehet. o Gépi kódú szinten csak két prefix létezik: REP, REPE, REPZ és REPNE, REPNZ, az els o prefix tehát különböz módon m ködik az ot követ utasítástól függ en. o u o o

79 A programhoz visszatérve, a REP MOVS . . . utasítás hatására megtörténik a közös karakterlánc átmásolása. Mivel a REP prefix CX-t l függ, nem volt véletlen, hogy mi is ebben tároltuk o a másolandó rész hosszát. Ezt a sort persze írhattuk volna az egyszer bb REP MOVSB alakban u is, de be akartuk mutatni az áloperandusokat is. Ha ez megvolt, akkor meg fogjuk mérni a Szoveg1 sztring hosszát, ami ekvivalens a 00h kódú karaktert megel z bájtok számának meghatározásával. Pontosan ezt fogjuk tenni mi is: o o megkeressük, hol van a lezáró karakter, annak offszetjéb l már meghatározható a sztring hossza. o A SCAS, SCASB, SCASW (SCAn String Byte/Word) utasítás teljes alakja a következ : o
SCAS célsztring

Az utasítás AL illetve AX tartalmát hasonlítja össze a cél ES:[DI] bájttal/szóval, beállítja a flageket, de egyik operandust sem bántja. Az összehasonlítást úgy kell érteni, hogy a célsztring elemét kivonja AL-b l vagy AX-b l, s az eredmény alapján módosítja a szükséges flag-eket. A o o SCASB, SCASW mnemonikok a SCAS BYTE/WORD PTR ES:[DI] utasítást rövidítik. Esetünkben egészen addig akarjuk átvizsgálni a Szoveg1 bájtjait, amíg meg nem találjuk a 00h kódú karaktert. Mivel nem tudjuk, mennyi a sztring hossza, így azt sem tudjuk, hányszor fog lezajlani a ciklus. CX-et tehát olyan értékre kell beállítani, ami biztosan nagyobb vagy egyenl a sztring hosszánál. Most feltesszük, hogy a sztring rövidebb 100 bájtnál. DF-et törölni o kell, mivel a sztring elejét l növekv sorrendben vizsgálódunk. A REPNE prefix hatására egéo o szen mindaddig ismétl dik a SCAS, amíg ZF = 1 lesz, ami pedig azt jelenti, hogy a legutolsó o vizsgált karakter a keresett bájt volt. Ha az utasítás végzett, DI a 00h-s karakter utáni bájtra fog mutatni, ezért DI-t 1-gyel csökkentjük, majd levonjuk bel le a Szoveg1 kezd offszetjét. Ezzel DI-ben kialakult a hossz, amit o o rögvest el is tárolunk. A kivonást kicsit cselesen oldjuk meg. Az STC utasítás CF-et állítja 1-re, amit a célból a forrással együtt kivonunk az SBB-vel. Az OFFSET operátor nevéb l adódóan a o t le jobbra álló szimbólum offszetcímét adja vissza. o A maradék programrész az el z ekhez képest semmi hasznosat nem csinál. A 100 db. o o szóból álló Vektor területet fogjuk feltölteni a következ módon: bármelyik szót úgy kapjuk o meg, hogy a kódszegmens egy adott bájtját el jelesen kiterjesztjük szóvá, majd ennek vesszük o az egyes komplemensét. Semmi értelme, de azért jó. :) Mivel a kódszegmens lesz a forrás, CS-t berakjuk DS-be, SI-t a szegmens elejére állítjuk. A ciklusban majdnem minden ismer s. A LODS, LODSB, LODSW és STOS, STOSB, o STOSW utasításokkal már találkoztunk korábban, de azért ismétlésképpen felidézzük m ködéu süket. Teljes alakjuk a következ : o
LODS STOS forrás cél

Forrás a szokott módon DS:SI, ebb l DS felülbírálható, a cél pedig ES:DI, ami rögzített. Az o utasítások AL-t vagy AX-et használják másik operandusként, a flag-eket nem módosítják. A LODS a forrást tölti be AL-be/AX-be, a STOS pedig AL-t/AX-et írja a cél területére. A LODSnél a forrás, míg a STOS-nál AL/AX marad változatlan. SI-t ill. DI-t a szokott módon frissítik. A LODSB, LODSW, valamint a STOSB, STOSW utasítások formálisan sorban a LODS BYTE/WORD PTR DS:[SI], ill. a STOS BYTE/WORD PTR ES:[DI] utasításokat rövidítik. Az AL-be beolvasott bájtot a CBW terjeszti ki el jelesen AX-be, majd ezt az egyoperandusú o NOT utasítás bitenként logikailag negálja (azaz képezi az egyes komplemensét), végül a STOS

utasítás a helyére teszi a kész szót. A ciklus lefutása után a program futása befejez dik. o

80 Az utasítások teljes alakjának használatát csak a forrás szegmens megváltoztatása indokolhatná, minden más esetben a rövid alakok célravezet bbek és jobban átláthatóak. Azonban az o egyszer bb változatok esetén is lehet ségünk van a forrás szegmens kiválasztására, mégpedig u o kétféle módon. Az egyik, hogy az utasítás el tt alkalmazzuk valamelyik SEGxx prefixet, a máo sik, hogy az utasítás el tti sorban kézzel berakjuk az adott prefix gépi kódját. Az els módszer o o MASM esetén nem m ködik (esetleg más assemblernél sem), a másodikban meg annyi a neu hézség, hogy tudni kell a kívánt prefix gépi kódját. Ezek alapján a LODS SS:[SI] utasítást az alábbi két módon helyettesíthetjük: 1.
SEGSS LODSB

2.
DB LODSB 36h

Az SS: prefix gépi kódja 36h, a LODSB-é 0ACh, így mindkett megoldás a 36h 0ACh bájtsoo rozatot generálja.

13. fejezet

Az .EXE és a .COM programok közötti különbségek, a PSP
Az, hogy a futtatható állományoknak milyen a formátumuk, az adott operációs rendszert l o függ. A .COM és .EXE fájlok a DOS operációs rendszer állományai, csak o képes ezeket futtatni.

13.1. A DOS memóriakezelése
Hamarosan megnézzük, hogy hogyan történik általában egy program elindítása, de hogy világosan lássunk, tudnunk kell egyet s mást a DOS-ról, azon belül is annak memóriakezelésér l. o Ugyebár 1 Mbájt memóriát tudunk megcímezni (így a DOS is), viszont ennyit mégsem tudunk kihasználni, ugyanis az 1 Mbájt memória fels területén (azaz a magas címtartományban) heo lyezkedik el pl. a BIOS programkódja (ráadásul az ROM memória) és a képerny memória, o tehát pl. ezeket nem tudjuk általános tárolásra használni. A memóriából mindenesetre összesen 384 Kbájtot tartanak fenn, így az 1024 Kbájtból csak 640 Kbájtot tud a DOS ténylegesen használni (ez a 640 ismer s szám kell, hogy legyen). Ezen a(z alsó) 640 Kbájton belül a DOS o memóriablokkokat (memóriaszeleteket) kezel, amelyeknek négy f jellemz jük van: o o · memóriablokk helye (kezd címe) o · memóriablokk mérete · memóriablokk állapota (foglalt/szabad) · memóriablokk (tulajdonosának) neve Ezekr l az adatokról a DOS nem vezet külön adminisztrációt egy saját, operációs rendszeri o memóriaterületen, ahol pl. különböz listákban tárolná oket (amihez természetesen senkinek o semmi köze), hanem egyszer en az adott memóriablokk elején helyezi el oket. Ezek az adatok u szintén egy blokkot alkotnak, amelynek mérete 16 bájt, neve pedig memóriavezérl blokk o (MCB ­ Memory Control Block). Ezzel megengedi a felhasználói programoknak is azt, hogy elérhessék ezeket az információkat, ami a DOS szemszögéb l elég nagy hiba, ugyanis bárki o ,,turkálhat" bennük, ami miatt tönkremehet a memória nyilvántartása. Ezalatt azt értjük, hogy adott esetben egy program a memóriablokkokra vonatkozó m veleteket nem a DOS szabványos u

13.2. ÁLTALÁBAN EGY PROGRAMRÓL

82

eljárásain keresztül végzi, hanem saját maga, mégpedig úgy, hogy egyszer en átírja az MCB-t, u és/vagy új(ak)at hoz létre, ha újabb memóriablokk(ok) keletkeztek. Egyébként három m veletet u értelmez a DOS a memóriablokkjain, mindegyik eljárásnak vannak hibakódjaik (visszatérési értékeik): Adott méretu memóriablokk lefoglalása (memória foglalása): Ha sikerült lefoglalni a kívánt méret memóriát, akkor visszakapjuk a lefoglalt terület u címét. Hibakódok: nincs elég memória, ekkor visszakapjuk a legnagyobb szabad blokk méretét is. Adott kezd címu blokk felszabadítása: o Hibakódok: a kezd cím nem egy blokk kezdetére mutat. o Adott kezd címu foglalt blokk méretének megváltoztatása: o Hibakódok: nincs elég memória (ha növelni akarjuk), érvénytelen blokkcím. Ha memóriát foglalunk le, akkor a DOS létrehoz egy új blokkot a kívánt mérettel, a blokk legelején az MCB-vel, s a blokk tényleges kezd címénél egy 16 bájttal nagyobb címet ad vissza o mint a lefoglalt memória kezdetét. A felhasználói program (aki a memóriát kérte) szempontjából az MCB tehát nem tartozik a blokkhoz, gondoljunk csak a magas szint programozási u nyelvekre: ott is használunk ilyen függvényeket, s bennünket egyáltalán nem érdekel, hogy a lefoglalt memóriát ,,ki" és milyen módon tartja nyilván. A DOS tehát eleve egy 16 bájttal nagyobb blokkot foglal le, hogy az MCB-t is el tudja helyezni benne. Lényeges, hogy soha egyetlen memóriablokk MCB-je se sérüljön meg, mindig helyes adatokat tartalmazzon, különben az említett memóriakezel eljárások a következ hibakóddal térnek o o vissza: az MCB-k tönkrementek. Ilyenkor természetesen az adott m velet sem hajtódik végre. u Erre példa: ha az adott blokk kezd címéhez hozzáadjuk annak méretét, akkor megkapjuk a o következ blokk kezd címét (pontosabban annak MCB-jének kezd címét), és így tovább, azt o o o kell kapnunk, hogy a legutolsó blokk utáni terület kezd címe a 640 Kbájt. Ha pl. ez nem telo jesül, az azt jelenti, hogy valamelyik (de lehet, hogy több) MCB tönkrement, s attól kezdve talán már olyan területeket vizsgáltunk MCB-ként, amik valójában nem is azok. Mindesetre ilyenkor használhatatlanná válik a nyilvántartás. A DOS is az említett (saját) eljárásokat használja. Ha az el bbi hibával találkozik, akkor azt úgy kezeli le, hogy a ,,Memory Allocation o Error ­ memóriakiosztási hiba" üzenettel szépen meghal. Bootoláskor a DOS lefoglal magának egy blokkot (a memória elején), oda rakja adott esetben a saját programkódját, a többi memóriát pedig bejelöli egyetlen nagy szabad blokknak, amelyet aztán a programok szabadon használhatnak.

13.2. Általában egy programról
Tekintsük át elméleti szempontból, hogy általában hogyan épül fel egy program, mire van szüksége a futáshoz és futás közben, aztán megnézzük, hogy mindezeket hogyan biztosították DOS alatt. Egy program három f részb l tev dik össze: o o o Kód: Ez alatt magát az alacsony szint programkódot értjük, a fordítóprogramok feladata, hogy u a magas szint nyelven leírt algoritmust a processzor számára feldolgozható kódra alakítu sák át.

13.2. ÁLTALÁBAN EGY PROGRAMRÓL

83

Adat: Ebben a részben helyezkednek el a program globális változói, azaz a statikusan lefoglalt memória. Verem: Magas szint nyelvekben nem ismeretes ez a fogalom (úgy értve, hogy nem a nyelv réu sze), viszont nélkülözhetetlen az alacsony (gépi) szinten. A fordítók pl. ennek segítségével valósítják meg a lokális változókat (amir l már szó volt). o A gyakorlatban legtöbbször a programkódot nem egyetlen összefügg egységként kezeljük o (és ez az adatokra is vonatkozik), hanem ,,csoportosítjuk" a program részeit, s ennek alapján kü lön szegmensekbe (logikailag különálló részekbe) pakoljuk oket. Pontosan ezt csináljuk akkor, amikor az assemblernek szegmenseket definiálunk (ez tehát az a másfajta szegmensfogalom, amikor a szegmenseket logikailag különválasztható programrészeknek tekintjük). Meg lehet még említeni a Pascalt is: o pl. a unitokat tekinti különálló programrészeknek, így minden unit programkódja külön kódszegmensbe kerül. Adatszegmens viszont csak egy globális van. Nézzük meg, hogy mely részeket kell eltárolnunk a programállományban! A programkódot mindenképpen, hiszen azt nem tudjuk a ,,semmib l" el állítani. A vermet o o viszont semmiképpen nem kell, hiszen a program indulásakor a verem üres (ezért mit is tárolnánk el?), ezért ezzel csak annyit kell csinálni, hogy a memóriában kijelölünk egy bizonyos nagyságú területet a verem számára, a veremmutatót a veremterület végére állítjuk (ott lesz a verem teteje). Szándékosan hagytuk a végére az adatokat, mert ennek elég csak valamely részét eltárolni. Elég csak azokat a globális változókat eltárolnunk, amelyeknek van valami kezdeti értéke a program indulásakor. Ezt úgy szokás megoldani, hogy a kezd értékkel nem rendelkez o o változókat egy külön adatszegmensbe rakjuk, s el írjuk (ezt meg lehet mondani az assemblero nek), hogy ez a szegmens ne kerüljön be az állományba (azaz a linker ne szerkessze be), viszont a program indításakor az operációs rendszer ,,tudjon" eme szegmensr l, s biztosítson neki meo móriát, azaz hozza létre azt. E megoldás azért kényelmetlen, mert ilyenkor két adatszegmensünk is van, de azt mi egyben szeretnénk látni, ezért a programnak futás közben ,,váltogatnia" kellene a 2 között. Kényelmesebb megoldás, ha egyetlen adatszegmenst deklarálunk, az egyik felében a kezd értékkel rendelkez változókat, a másik felében pedig az azzal nem rendelkez változókat o o o helyezzük el, s azt írjuk el , hogy a szegmens másik fele ne kerüljön be az állományba (ez is o megoldható, hiszen a szegmenseket szakaszokban is megadhatjuk, így megmondhatjuk azt is az assemblernek, hogy a szegmens adott darabja ne foglaljon feleslegesen helyet az állományban. Ez persze csak akkor teljesülhet, ha abban a szegmensben ténylegesen úgy deklaráljuk a változókat, hogy ne legyen kezd értéke (pl. Nev DB ?), ha ez a követelmény valahol nem teljesül, o akkor mindenképpen bekerül az egész szegmensdarab az állományba). Egy program indításakor a programállományból tehát meg kell tudnia az operációs rendszer nek (mivel o indítja a programokat), hogy hol található(ak) az állományban a kódot tartalmazó részek. Ha ez megvan, lefoglalja nekik a megfelel mennyiség memóriá(ka)t (ott lesznek elheo u lyezve a program kódszegmensei), majd oda betölti a programkódot. Ezután következik az adat. Hasonlóan jár el a program adataszegmenseivel (az állományban le nem tárolt szegmenseket is beleértve, ezen memóriabeli szegmensekbe nyilván nem tölt be semmit az állományból, így az itt található változóknak meghatározatlan lesz a kezd értéke), az adott szegmensek tartalo mát szintén bemásolja. A memóriában meglesznek tehát a program adatszegmensei is. Azután foglal memóriát a veremnek is (veremszegmens). A veremmutatót ráállítja a veremszegmens végére, az adatszegmens-regisztert pedig valamely adatszegmens elejére (ezzel a program el o van készítve a futásra), majd átadja a vezérlést (ráugrik) a program els utasítására valamelyik o kódszegmensben.

13.3. AHOGY A DOS INDÍTJA A PROGRAMOKAT

84

Mindez, amit leírtunk, csak elméleti dolog, azaz hogyan végzi el egy operációs rendszer általában egy program elindítását. Azonban, mint azt látni fogjuk, a DOS ilyen nagy fokú szabadságot nem enged meg számunkra.

13.3. Ahogy a DOS indítja a programokat
A DOS-nak kétféle futtatható állománya van (h ha!), a .COM és a .EXE formátumú. Amit u ebben a fejezetben általánosságban leírunk, az mind a kett re vonatkozik. A kett közti részletes o o különbséget ezután tárgyaljuk. Mint az látható, az, hogy a program mely szegmense tulajdonképpen mit tartalmaz, azaz hogy az kód- vagy adatszegmens-e, az teljesen lényegtelen. A DOS viszont ennél is továbbmegy: a programállományaiban letárolt szegmensekr l semmit sem tud! Nem tudja, hogy hány o szegmensr l van szó, így pl. azt sem, hogy melyik hol kezd dik, mekkora, stb., egyetlen egy o o valamit tud róluk, mégpedig azt, hogy azok összmérete mennyi. Éppen ezért nem is foglal mindegyiknek külön-külön memóriát, hanem megnézi, hogy ezek mindösszesen mekkora memóriát igényelnek, s egy ekkora méret memóriablokkot foglal le, ezen belül kap majd helyet minden u szegmens. Annak, hogy a szükséges memóriát egyben foglalja le, annyi hátránya van, hogy ha nincs ekkora méret szabad memóriablokk, akkor a programot nem tudja futtatni (ekkor kiköpi u a ,,Program too big to fit in memory ­ a program túl nagy ahhoz, hogy a memóriába férjen" hibaüzenetet), holott lehet, hogy a szükséges memória ,,darabokban" rendelkezésre állna. Igazából azonban a gyakorlatban ez a probléma szinte egyáltalán nem jelentkezik, mivel ez csak a memória felaprózódása esetén jelenhet meg, azaz akkor, ha egyszerre több program is fut, aztán valamely(ek) végetér(nek), az általuk lefoglalt memóriablokk(ok) szabaddá válik/válnak, így a végén egy ,,lyukacsos" memóriakép keletkezik. Mivel a DOS csak egytaszkos oprendszer, azaz egyszerre csak egy program képes futni, mégpedig amelyiket a legutoljára indítottak, így nyilván az ot indító program nem tud azel tt befejez dni, miel tt ez befejez dne. Ebb l az o o o o o következik, hogy a szabad memória mindig egy blokkot alkot, a teljes memória foglalt részének mérete pedig a veremelv szerint változik: amennyit hozzávettünk a foglalt memória végéhez, azt fogjuk legel ször felszabadítani. Persze azért a DOS esetében is felaprózódhat a memória, ha o egy program dinamikusan allokál magának memóriát, aztán bizonyos lefoglalt területeket tetsz leges sorrendben (és nem a lefoglalás fordított sorrendjében) szabadít fel. Ha ez a program o ebben az állapotban saját maga kéri az DOS-t egy újabb program indítására, akkor már fennáll(hat) az el bbi probléma. Elkalandoztunk egy kicsit, ez már inkább az operációs rendszerek o tárgy része, csak elmélkedésnek szántuk. A DOS tehát egyetlenegy memóriablokkot foglal le minden szegmens számára, ezt az egész memóriablokkot nevezik programszegmensnek (Program Segment) (már megint egy újabb szegmensfogalom, de ez abból adódik, hogy a szegmens szó darabot, szeletet jelent, s ezt ugyebár sok mindenre lehet érteni). Fontos, hogy ezekkel a fogalmakkal tisztában legyünk. Mint arról már szó volt, a programszegmens akkora méret , amekkora a szegmensek által u összesen igényelt memória. Nos, ez nem teljesen igaz, ugyanis a programszegmens legalább ekkora (+256 bájt, ld. kés bb), ugyanis ha a programindításkor a DOS egy olyan szabad memóo riablokkot talál, ami még nagyobb is a szükségesnél, akkor is ,,odaadja" az egészet a programnak. Hogy ez mire jó, azt nem tudjuk, mindenesetre így van. Ebb l az a kellemetlenség adódik, o hogy pl. ha a program a futás során dinamikusan szeretne memóriát allokálni, akkor azt a hibaüzenetet kaphatja, hogy nincs szabad memória. Valóban nincs, mert az összes szabad memória a programszegmenshez tartozhat, a programszegmens meg ugyebár nem más, mint egy foglalt memóriablokk, s a DOS memóriafoglaláskor nem talál más szabad blokkot. Ezért ilyenkor a

13.4. .COM ÁLLOMÁNYOK

85

programnak induláskor le kell csökkentenie a programszegmensének méretét a minimálisra a már említett funkcióval! Még egy fontos dolog, amit a DOS minden program indításakor elvégez: a programszegmens elején létrehozza a programszegmens-prefixet (Program Segment Prefix ­ PSP), ami egy 256 bájtból álló információs tömb. A +256 bájt tehát a PSP miatt kell. Mire kell a PSP és mit tartalmaz? Sok mindent. Ezen 256 bájtos terület második fele pl. azt a sztringet tartalmazza, amit a promptnál a program neve után írunk paraméternek. Azt, hogy az els 128 bájt miket tárol, o azt nagyon hosszú lenne itt felsorolni (inkább nézzük meg egy könyvben), számunkra nem is igazán fontos, csak egy-két érdekesebb dolog: az els két bájt az INT 20h kódja (0CDh 20h), o vagy pl. a környezeti sztringek (amelyeket a DOS SET parancsával állíthatunk be) számára is le van foglalva egy memóriablokk, ennek a szegmenscíme (ami azt jelenti, hogy a cím offszetrésze nulla, így azt nem kell letárolni, csak a cím szegmensrészét, de ez igaz minden más memóriablokk címére is) benne van a PSP-ben. Így a program kiolvashatja ezt, s hozzáférhet a környezeti sztringekhez, módosíthatja azokat, stb. A DOS saját maga számára is tárolgat információt: pl. megtalálható az adott programot elindító program PSP-jének szegmenscíme is (visszaláncolás ­ backlink), ami általában a parancsértelmez é (COMMAND.COM), hiszen o legtöbbször onnan indítjuk a programokat. Mire kell ez? Arra, hogy ha a program befejez dik, o akkor a DOS tudja, hogy ,,kinek" kell visszaadnia a vezérlést, és ez nyilván a programot betölt o program. Ezek után nézzük meg a különbséget .COM és .EXE között.

13.4. .COM állományok
Ezek a fájlok voltak a DOS els futtatható állományai, nagyon egyszer felépítés ek, ugyanis o u u olyan programok számára tervezték, amelyek egyetlen szegmensb l állnak. Err l ugyan nem o o volt szó, de egy szegmensnek természetesen lehet vegyes jellege is, tartalmazhat kódot is és adatot is egyszerre. Nos, .COM-ok esetében még a veremterület is ebben a szegmensben kell, hogy elhelyezkedjen. Egy programnak illene minimum 3 szegmensének lennie a három programrész számára, de itt szó sincs err l. Mint tudjuk, egy szegmens max. 64 Kbájt lehet, így egy .COM o program is. Ráteszünk azonban még egy lapáttal: a PSP is a szegmens része, indításkor a DOS a PSP mögé másolja be a .COM programot! (Ezért van az, hogy .COM programok írásakor a szegmens elejére be kell raknunk azt a bizonyos ORG 100h direktívát, ezzel jelezve az assemblernek, hogy minden szegmensbeli offszetcímhez ,,adjon hozzá" 100h-t, azaz az offszeteket tolja el 256 bájttal, mivel a PSP nyilván nem tárolódik el a programállományban, de mindig ,,oda kell képzelnünk" a szegmens elejére). Induláskor a DOS a verembe berak egy 0000h-t, ami a PSPben lev INT 20h utasítás offszetcíme. A kilépés tehát megoldható egy RET utasítással is, mivel o a DOS a programot egy közeli eljárásnak tekinti. (Ez csak .COM-ok esetén alkalmazható, mert ehhez a CS-nek a PSP-re kell mutatnia). Ezután programszegmensen a programszegmens csak azon részét értjük, amelyet a program ténylegesen használ, nem vesszük bele tehát a felesleges részt, mivel annak semmi szerepe. A .COM programok programszegmense mindig 64 Kbájt, eltekintve attól az (egyébként nagyon ritka) esett l, amikor már 64 Kbájt szabad memória sincs a program számára, de ezzel most o nem foglalkozunk. Miért van az, hogy a szegmens mindig kib vül 64 Kbájtra? Azért, hogy o minél több hely legyen a verem számára. Ugyanis a .COM fájlokban a programról semmi kísér információ nem található, a fájl csak magát az egyetlen szegmenst tárolja le, így nem o tudjuk megadni a DOS-nak, hogy a szegmensen belül hol a veremterület, azaz hogy mi legyen

13.5. RELOKÁCIÓ

86

az SP kezdeti értéke. Emiatt a DOS úgy gondolkodik, hogy automatikusan maximális vermet biztosít, ezért növeli meg a szegmensméretet 64 Kbájtra, s annak végére állítja a veremmutatót. Ebb l az is következik, hogy a program írásakor nekünk sem kell tör dnünk a veremmel. o o Ezek szerint azt sem tudjuk megadni, hogy a szegmensben hol található az els végrehajtandó o utasítás. Így van, ennek mindig a .COM fájl elején kell lennie, azaz a memóriabeli szegmens 256. bájtján. Indításkor a DOS tehát a fenti ábra szerint állítja be a regisztereket.

13.5. Relokáció
Ha egy program több szegmenst is tartalmaz, akkor ahhoz, hogy el tudja érni azokat, meg kell határoznia minden egyes szegmens tényleges szegmenscímét. Azért nevezik ezt a folyamatot relokációnak (áthelyezésnek), mert a szegmenseknek csak a program elejéhez képesti relatív elhelyezkedését ismerhetjük (vehetjük úgy is, hogy a program legeleje a 0-n, azaz az 1 Mbájtos memória elején kezd dik, ekkor a szegmensek relatív címei egyben abszolút címek is, o a program futóképes lenne), azonban a program a memória szinte tetsz leges részére betölt do o het, s hogy pontosan hova, az csak indításkor derül ki. A program csak akkor lesz futóképes, ha a relatív szegmenscímeket átírjuk, azaz azokhoz hozzáadjuk a program elejének szegmenscímét. Ezzel a programot mintegy áthelyeztük az adott memóriaterületre. Lássuk ezt egy példán keresztül:
Kod SEGMENT PARA ASSUME CS:Kod,DS:Adat ;paragrafushatáron kez ;dodjön a szegmens ;mondjuk meg az assem;blernek, hogy mely szeg;mensregiszter mely ;szegmensre mutat, ;o eszerint generálja ;majd az ;offszetcímeket ;Az ,,Adat" szegmens ;szegmenscíme ;A feltételezésnek ;tegyünk is eleget, ;ne vágjuk át szegény ;assembler bácsit

MOV MOV

AX,Adat DS,AX

· · ·
MOV INT ENDS AX,4C00h 21h

Kod Adat Valami Akarmi Adat

SEGMENT PARA DB DB ENDS 2 14

;paragrafushatáron kez ;dodjön a szegmens

13.5. RELOKÁCIÓ

87
END

Ez egy szokványos program, de a lényeg most azon van, hogy amikor az elején a DS-t ráállítjuk az Adat szegmensre, akkor a MOV AX,Adat utasítással mit kezd az assembler, hiszen az Adat értéke (ami egy szegmenscím) attól függ, hogy a program a memóriába hová tölt dik majd o be. A válasz az, hogy az Adat egy relatív érték, a program els szegmenséhez képest. Hogy o melyik lesz az els szegmens, az csak a linkelésnél válik el, amikor a linker az egyes szego mensdarabokat összef zi egyetlen szegmenssé, valamint a szegmenseket egyetlen programmá, u de hogy a szegmensek milyen sorrendben kövessék egymást a programban, arról o dönt. Az assembler tehát csak bejelöli az .OBJ fájlban, hogy ezen a helyen egy relatív szegmenscím kelletik, ezt a linker észreveszi, s beírja oda azt. A fenti programnak csak két szegmense van, tegyük fel, hogy a Kod lesz az els , s hogy a Kod szegmens mérete pl. 36 bájt (mivel ez nem o lenne 16-tal osztható, ezért a méret linkeléskor 16-tal oszthatóra n , hogy az utána következ o o szegmens paragrafushatárra essen, így most 48 bájt lesz). Ekkor a lefordított program a következ lesz: o
MOV MOV AX,0003h DS,AX ;48/16=3

· · ·
MOV INT AX,4C00h 21h

· · ·

;Ezután szükség szerint ;,,szemét" következik, ;hogy a ,,DB 2" ;a program ;elejéhez képest 16-tal ;osztható címre essen, ; hiszen azt a szegmenst ;paragrafushatáron ;kezdtük

DB DB

2 14

Most a program egy relatív szegmenscímet tartalmaz (0003), de még miel tt elindíthatnánk, o azt át kell javítani a tényleges címre. Ha pl. a Kod szegmens a memória 5678h:0000h területére kerül majd, akkor a 0003h-hoz hozzá kell adni az 5678h-t, hogy helyes értéket kapjunk. Mivel ezt a program indításakor kell elvégezni, így ez a feladat a DOS-ra hárul, de o ezt csak az .EXE fájlok esetében képes elvégezni, mivel ott el van tárolva a relokációhoz szükséges információ. Ebb l világos, hogy egy .COM program nem tartalmazhat ilyen módon relatív o szegmenscímeket, igy pl. a fenti programot sem lehet .COM-má fordítani, a linker is idegeskedik, ha ilyennel találkozik. A másik megoldás az, ha a program maga végzi el induláskor a szükséges relokációt. Ezzel elérhetjük azt is, hogy egy .COM program több szegmenst is tartalmazzon (amelyek összmérete nem haladhatja meg persze a 64K-t), csak a relokációt el kell végeznünk. A fenti példát átírva:

13.6. .EXE ÁLLOMÁNYOK

88

DOSSEG Kod SEGMENT PARA ASSUME CS:Kod,DS:Adat ORG 100h

;PSP helye

@Start: MOV AX,Kod:Adat_eleje ;a ,,Kod" szegmens elejé;hez képest az ;,,Adat_eleje" címke, ;azaz az adat szegmens ;eleje hány bájtra helyez;kedik el ;osztva 16-tal (az elozo ;példában közvetlenül ez ;az érték volt benne az ;utasításban) ;DS ekkor még a PSP ;elejére, azaz a ,,Kod" ;szegmens elejére ;mutat ;ráállítjuk az adatszeg;mensre ;innen DS már az adat ;szegmensre mutat

MOV SHR

CL,4h AX,CL

MOV

BX,DS

ADD MOV

AX,BX DS,AX

· · ·
MOV INT ENDS AX,4C00h 21h

Kod Adat

SEGMENT PARA ORG 0h LABEL DB DB ENDS END 2 14 @Start

;Ezen a szegmensen belül ;nincs eltolás

Adat_eleje Valami Akarmi Adat

A program elején lev DOSSEG direktívával biztosítjuk azt, hogy a szegmensek a deklaráo lás sorrendjében kövessék egymást az összeszerkesztett programban is, hiszen itt fontos, hogy a Kod legyen az els szegmens. o

13.6. .EXE állományok
Az .EXE állományokat a bonyolultabb programok számára találták ki, amelyek tetsz leges o számú szegmensb l állnak. Magában az állományban ezek egymást követ en, folyamatosan o o vannak eltárolva, ahogyan a linker egymás után f zte oket. Fontos azonban megjegyezni, hogy u

13.6. .EXE ÁLLOMÁNYOK

89

nem biztos, hogy az egész állomány magát a programot tárolja, mivel az állományban a program után még tetsz leges hosszúságban tárolhatunk pl. adatokat, mint egy közönséges adatfájlban. o Ezek az adatok nem tartoznak a programhoz, még egyszer kihangsúlyozzuk. Az .EXE fájlok elején mindig található egy fejléc (header), azaz egy információs tömb, amely a DOS számára nélkülözhetetlen a program elindításához. A betöltési folyamat hasonló a .COM-okéhoz: a fejlécb l a DOS megnézi, hogy a fejléc o után eltárolt program milyen hosszú, azaz hol a határ a program és az el bb említett adatok köo zött. Itt persze már nem él az a megkötés, hogy a program (azaz a szegmensek összmérete) nem haladhatja meg a 64 Kbájtot. Ezután szintén a fejlécb l megnézi, hogy a programnak mennyi o többletmemóriára van szüksége (ez a bejegyzés a ,,minimálisan szükséges többletmemória"). Ez biztositja a programban el nem tárolt szegmensek létrehozását a memóriában, amir l már o beszéltünk. A programszegmens így legalább az összméret + többletmemória méret lesz. Van u azonban egy ,,maximálisan szükséges többletmemória"-bejegyzés is, elméletileg ennél nagyobb soha nem lehet nagyobb a programszegmens, ezzel tehát el lehetne kerülni, hogy az feleslegesen nagy legyen, de a gyakorlatban egyáltalán nem élnek ezzel a lehet séggel, szinte mindig o maximumra állitják (a linkerek, merthogy ok hozzák létre a futtatható állományokat). A DOS tehát lefoglalja a programszegmenst, bemásolja az .EXE-b l a programot, s elvégzi a program o relokációját. Hogy a programban mely helyeken kell átírni a relatív szegmenscímeket, azt a relokációs táblából (relocation table) (ami szintén a fejléc része) tudja az oprendszer. Mivel programunk több szegmensb l áll, kijelölhetjük, hogy melyik a veremszegmens (szintén relatío van), indításkor a DOS az SS:SP-t ennek megfelel en állítja be. Ugyanígy relatívan adhatjuk o meg, hogy hol van a program belépési pontja (entry point), azaz mi lesz a CS:IP kezdeti értéke. Fontos megjegyezni, hogy .EXE-k esetében a PSP nem tartozik semelyik szegmenshez sem, ezért nincs szükség ilyen programok írásakor sehol sem az ORG 100h direktíva megadására. Kezdeti adatszegmenst nem tudunk kijelölni a program számára, a DOS úgy van vele, hogy ha egy programnak több szegmense is van, akkor azok között valószín leg egynél több u adatszegmens is van, így a programnak futás közben úgyis állítgatnia kell a DS-t, akkor miért ne tegye ezt meg pl. már rögtön induláskor, ahogy az els példában már láttuk? Ez amúgy sem o okoz a programnak gondot, a relokációt elvégzik helyette, nem neki kell vele szenvednie. Éppen ezért kezdetben a DS, ES a PSP elejére mutat.

14. fejezet

Szoftver-megszakítások
Mint említettük, szoftver-megszakításnak egy program által kiváltott megszakítást nevezünk. Hogy miért van erre szükség? Nos, néhány szoftver-megszakítás jócskán megkönnyíti a különféle hardvereszközök elérését, míg mások az operációs rendszer egyes funkcióival teremtenek kapcsolatot, de számos egyéb felhasználásuk is lehetséges. Összesen 256 db. szoftver-megszakítást kezel a 8086-os mikroprocesszor. Ebb l 8 (vagy o 16) db. az IRQ-k kezelésére van fenntartva, néhány pedig speciális célt szolgál, de a többi mind szabadon rendelkezésünkre áll. Azt azért tudni kell, hogy a gép bekapcsolása, ill. az operációs rendszer felállása után néhány megszakítás már foglalt lesz, azokon keresztül használhatjuk a munkánkat segít szolgáltatásokat. o Megszakítást az egyoperandusú INT utasítással kezdeményezhetünk. Az operandus kizárólag egy bájt méret közvetlen adat lehet, ami a kért szoftver-megszakítást azonosítja. Az utasítás u pontos m ködésével a következ fejezetben foglalkozunk majd. Érdemes megemlíteni még egy u o utasítást. Az INTO (INTerrupt on Overflow) egy 04h-s megszakítást kér, ha OF = 1, különben pedig m ködése megfelel egy NOP-nak (azaz nem csinál semmit sem, csak IP-t növeli). u Ennek akkor vehetjük hasznát, ha mondjuk az el jeles számokkal végzett m veletek során keo u letkez túlcsordulás számunkra nemkívánatos, és annak bekövetkeztekor szeretnénk lekezelni a o szituációt. Egy adott számú megszakítás sokszor sok-sok szolgáltatás kapuját jelenti. A kívánt szolgáltatást és annak paramétereit ilyenkor különböz regiszterekben kell közölni, és a visszatér o o értékeket is általában valamilyen regiszterben kapjuk meg. Hogy melyik megszakítás mely szolgáltatása hányas számú és az milyen regiszterekben várja az adatokat, lehetetlen fejben tartani. Ezeknek sok szakkönyvben is utánanézhetünk. Ha viszont a könyv nincs a közelben, akkor sem kell csüggedni. Számos ingyenes segédprogramot kifejezetten az ilyen gondok megoldására készítettek el. A legismertebbek: HelpPC, Tech Help!, Norton Guide, Ralf Brown's Interrupt List. A programok mindegyike hozzáférhet , és tudtunkkal magáncélra ingyenesen o használható. Mindegyik tulajdonképpen egy hatalmas adatbázison alapul, amiben hardveres és szoftveres témákat is találhatunk kedvünk szerint. A 14.1. táblázatban felsorolunk néhány gyakoribb, fontos megszakítást a teljesség igénye nélkül. A következ kben néhány példán keresztül bemutatjuk a fontosabb szolgáltatások használao tát.

14.1. SZÖVEGKIÍRATÁS, BILLENTYUZET-KEZELÉS

91

14.1. táblázat. Gyakran használt szoftver-megszakítások 10h 13h 14h 16h 1Ch 20h 21h 25h 26h 27h 2Fh 33h Képerny vel kapcsolatos szolgáltatások (Video services) o Lemezm veletek (Disk operation) u Soros ki-/bemenet (Serial I/O) Billenty zet (Keyboard) u Id zít (Timer tick) o o Program befejezés (Program terminate ­ DOS) DOS szolgáltatások (DOS services) Közvetlen lemezolvasás (Absolute disk read ­ DOS) Közvetlen lemezírás (Absolute disk write ­ DOS) Rezidenssé tétel (Terminate and stay resident ­ DOS) Vegyes szolgáltatások (Multiplex) Egér támogatás (Mouse support)

14.1. Szövegkiíratás, billentyuzet-kezelés
Legyen a feladat a következ : a program írja ki a képerny re a ,,Tetszik az Assembly?" o o szöveget, majd várjon egy billenty lenyomására. Ha az ,,i" vagy ,,I" gombot nyomják le, akkor u írja ki az ,,Igen." választ, ,,n" és ,,N" hatására pedig a ,,Nem." szöveget. Mindkét esetben ezután fejezze be m ködését. Egyéb billenty re ne reagáljon, hanem várakozzon valamelyik helyes u u válaszra. Lássuk a programot:
Pelda11.ASM:
MODEL SMALL .STACK ADAT Kerdes Igen Nem ADAT KOD SEGMENT DB "Tetszik az Assembly? $" DB "Igen.",0Dh,0Ah,'$' DB "Nem.",0Dh,0Ah,'$' ENDS SEGMENT ASSUME CS:KOD,DS:ADAT

@Start: MOV MOV MOV LEA INT @Ciklus: XOR INT CMP JE CMP AH,AH 16h AL,'i' @Igen AL,'I' AX,ADAT DS,AX AH,09h DX,[Kerdes] 21h

14.2. SZÖVEGES KÉPERNYO KEZELÉSE

92
@Igen AL,'n' @Nem AL,'N' @Ciklus DX,[Nem] @Vege DX,[Igen] AH,09h 21h AX,4C00h 21h @Start

JE CMP JE CMP JNE @Nem: LEA JMP @Igen: LEA @Vege: MOV INT MOV INT ENDS END

KOD

A program elég rövid, és kevés újdonságot tartalmaz, így megértése nem lesz nehéz. Szöveget sokféleképpen ki lehet írni a képerny re. Mi most az egyik DOS-funkciót fogjuk o használni erre. Már láttuk, hogy a DOS-t az INT 21h-n keresztül lehet segítségül hívni, a kért szolgáltatás számát AH-ban kell megadnunk. A 4Ch sorszámú funkciót már eddig is használtuk a programból való kilépésre. A 09h szolgáltatás egy dollárjellel (,,$") lezárt sztringet ír ki az aktuális kurzorpozícióba. A szöveg offszetcímét DX-ben kell megadni, szegmensként DS-t használja. A billenty zet kezelését az INT 16h-n keresztül tehetjük meg. A szolgáltatás számát itt is u AH-ba kell rakni. Ennek 00h-s funkciója egészen addig várakozik, míg le nem nyomunk egy gombot a billenty zeten, majd a gomb ASCII-kódját visszaadja AL-ben. Hátránya, hogy néhány u billenty lenyomását nem tudjuk így megfigyelni (pl. Ctrl, Shift), míg más billenty k AL-ben u u 00h-t adnak vissza, s közben AH tartalmazza a billenty t azonosító számot (ez az ú.n. scan u code). Most nekünk csak a kicsi és nagy ,,I" ill. ,,N" bet kre kell figyelnünk, így nincs más u dolgunk, mint a megszakításból visszatérés után AL-t megvizsgálnunk. Ha AL-ben 'i' vagy 'I' van, akkor a @Igen címkére megyünk. Ha AL = 'n' vagy AL = 'N', akkor a @Nem címkét választjuk célként. Különben visszamegyünk a @Ciklus címkére, és várjuk a következ lenyoo mandó billenty t. Figyeljük meg, hogy a szelekció utolsó feltételében akkor megyünk vissza a u ciklusba, ha AL 'N', máskülönben ,,rácsorgunk" a @Nem címkére, ahová amúgy is mennünk kéne. Ezzel a módszerrel megspóroltunk egy JMP-t. Akár a @Igen, akár a @Nem címkét választottuk, a szöveg offszetjének DX-be betöltése után a @Vege címkénél kötünk ki, ahol kiírjuk a választ, majd kilépünk a programból.

14.2. Szöveges képerny kezelése, számok hexadecimális alako ban kiírása
Következ problémánk már rafináltabb: a program indításkor törölje le a képerny t, majd o o a bal fels sarokba folyamatosan írja ki egy szó méret változó tartalmát hexadecimálisan, mio u közben figyeli, volt-e lenyomva billenty . A változó értékét minden kiírást követ en eggyel u o növelje meg, kezdetben pedig a 0000h-ról induljon. Ha volt lenyomva billenty , akkor annak u

14.2. SZÖVEGES KÉPERNYO KEZELÉSE

93

ASCII kódja szerinti karaktert írja ki a második sorba. A szóköz (space) megnyomására lépjen ki.
Pelda12.ASM:
MODEL SMALL .STACK ADAT Szamlalo HexaJegy ADAT KOD SEGMENT DW 0000h DB "0123456789ABCDEF" ENDS SEGMENT ASSUME CS:KOD,DS:ADAT PROC PUSH LEA MOV MOV SHR XLAT XCHG AND XLAT MOV PUSH XCHG MOV SHR XLAT XCHG AND XLAT MOV MOV INT MOV INT POP INT MOV INT POP RET ENDP PROC PUSH MOV

HexKiir

AX BX CX DX BX,[HexaJegy] CL,4 DL,AL AL,CL AL,DL AL,0Fh DH,AL DX AL,AH DL,AL AL,CL AL,DL AL,0Fh DH,AL AH,02h 21h DL,DH 21h DX 21h DL,DH 21h DX CX BX AX

HexKiir Torol

AX BX CX DX AX,0600h

14.2. SZÖVEGES KÉPERNYO KEZELÉSE

94
BH,07h CX,CX DX,184Fh 10h DX CX BX AX

Torol GotoXY

MOV XOR MOV INT POP RET ENDP PROC PUSH MOV XOR INT POP RET ENDP

AX BX AH,02h BH,BH 10h BX AX

GotoXY @Start:

MOV MOV CALL @Ciklus: XOR CALL MOV CALL INC MOV MOV INT JZ CMP JE INC CALL MOV MOV INT XOR INT JMP @Vege: XOR INT MOV INT ENDS END

AX,ADAT DS,AX Torol DX,DX GotoXY AX,[Szamlalo] HexKiir AX [Szamlalo],AX AH,01h 16h @Ciklus AL,20h @Vege DH GotoXY AH,02h DL,AL 21h AH,AH 16h @Ciklus AH,AH 16h AX,4C00h 21h @Start

KOD

Az adatszegmensben nincs semmi ismeretlen, bár a HexaJegy tartalma kicsit furcsának t nhet. A homályt rövidesen el fogja oszlatni, ha megnézzük, hogyan írjuk ki a számot. u A HexKiir az AX-ben lev el jeltelen számot írja ki négy karakteres hexadecimális alakban o o

14.2. SZÖVEGES KÉPERNYO KEZELÉSE

95

(tehát a vezet nullákkal együtt). Az eljárásban a szó kiírását visszavezetjük két bájt hexadecio mális kiírására, azt pedig az alsó és fels bitnégyesek számjeggyé alakítására. Mivel a képero ny re el ször a fels bájt értékét kell kiírni, az alsó bájtot dolgozzuk fel, amit aztán berakunk a o o o verembe, majd a fels bájt hasonló átalakításai után azt kiírjuk a képerny re, végül a veremb l o o o kivéve az alsó bájt hexadecimális alakja is megjelenik a helyén. DX-ben fogjuk tárolni a már elkészült számjegyeket. BX és CL szerepére hamarosan fény derül. El ször a fels bitnégyest kell számjeggyé alakítanunk. Ezért AL-t elrakjuk DL-be, majd AL o o felveszi a fels bitnégyes értékét (ami 00h és 0Fh között lesz). Ezt az SHR AL,CL utasítással o érjük el. Most CL = 4 (erre állítottuk be), s így AL fels négy bitje lekerül az alsó négy heo lyére, miközben a fels bitnégyes kinullázódik (ez a shiftelés tulajdonsága). Ez pedig azt fogja o jelenteni, hogy AL-ben ugyanakkora szám lesz, mint amekkora eredetileg a fels bitnégyesében o volt. Az operandus nélküli XLAT (transLATe byte; X ­ trans) utasítás az AL-ben lev bájtot kio cseréli a DS:BX cím fordítótáblázat AL-edik elemére (nullától számozva az elemeket), tehát u szimbolikusan megfelel a MOV AL,[BX+AL] utasításnak (ami ugyebár így illegális), de AL-t el jeltelenül értelmezi. Egyetlen flag tartalmát sem módosítja. Az alapértelmezett DS szego mens felülbírálható prefixszel. Esetünkben ez most azt jelenti, hogy az AL-ben lev számot o az azt szimbolizáló hexadecimális számjegyre cseréli le, hiszen BX a HexaJegy offszetcímét tartalmazza. Így már világos a HexaJegy definíciója. Az XLAT utasítás egyébként kaphat egy ,,áloperandust" is, ennek szerepe hasonló a sztringkezel utasítások operandusaihoz. o Érdekesség, hogy az XLAT utasítás az XLATB mnemonik alatt is elérhet , tehát mindkett o o ugyanazt jelenti a gépi kód szintjén. Hogy miért kell egy ilyen egyértelm dolgot végz utau o sításnak 2 mnemonik, azon lehetne filozofálni. Az SHL-SAL párosra még rá lehet fogni, hogy segítik a program dokumentálását (t.i. az operandus típusát jelzi a mnemonik), viszont a mostani esetben érthetetlen a dupla mnemonik létezése. Most, hogy megvan az els jegy, AL eredeti alsó bitnégyesét is át kell alakítani számjeggyé. o AL-t DL-ben tároltuk el, és az elején azt mondtuk, hogy a kész számjegyeket DX-ben fogjuk gy jteni. Most tehát célszer lenne, ha AL és DL tartalmát fel tudnánk cserélni. Erre jó a u u kétoperandusú XCHG (eXCHanGe) utasítás, ami a két operandusát felcseréli. Az operandusok vagy 8, vagy 16 bites regiszterek, vagy egy regiszter és egy memóriahivatkozás lehetnek. A flag-eket persze nem bántja. Speciális esetnek számít, ha AX-et cseréljük fel valamelyik másik 16 bites általános regiszterrel, ez ugyanis csak 1 bájtos m veleti kódot eredményez. Az XCHG u AX,AX utasítás a NOP (No OPeration) operandus nélküli mnemonikkal is elérhet . o AL tehát ismét az eredeti értékét tartalmazza. Most a fels bitnégyest kellene valahogy o leválasztani, törölni AL-ben, hogy ismét alkalmazhassuk az XLAT-ot. Ezt most az AND utasítás végzi el, ami ugyebár a céloperandust a forrásoperandussal logikai ÉS kapcsolatba hozza, majd az eredményt a cél helyén tárolja. Most AL-t a 0Fh értékkel hozza ÉS kapcsolatba, ami annyit fog jelenteni, hogy a fels bitnégyes törl dik (hiszen Valami AND 00. . . 0b = 0h), az alsó pedig o o változatlan marad (mivel Valami AND 11. . . 1b = Valami). AL-t most már átalakíthatjuk számjeggyé (XLAT), majd miután DH-ban eltároltuk, DX-et berakjuk a verembe. AH még mindig az eredeti értékét tartalmazza, ideje hát ot is feldolgozni. Az XCHG AL,AH után (ami most MOV AL,AH is lehetne, hiszen a szám alsó felére többé már nincs szükségünk) az el z khöz hasonlóan el állítjuk a két számjegyet DX-ben. Ha ez is o o o megvan, akkor nincs más hátra, mint a négy számjegyet kiírni. Ezt a már jól ismert 02h számú INT 21h szolgáltatással tesszük meg. A 14.1. táblázatban látható, hogy az INT 10h felel s a megjelenítéssel kapcsolatos szolgálo tatásokért. Így logikus, hogy ehhez a megszakításhoz fordulunk segítségért a képerny letöro léséhez. A Torol eljárás nagyon rövid, ami azért van, mert a törlést egyetlen INT 10h hívással

14.3. MUNKA ÁLLOMÁNYOKKAL

96

elintézhetjük. A 06h-s szolgáltatás egy szöveges képerny ablak felfelé görgetésére (scrolling) o szolgál, és úgy m ködik, hogy az AL-ben megadott számú sorral felfelé csúsztatja a képerny u o tartalmát, az alul megüresed sorokat pedig a BH-ban megadott szín (attribútumú) szóközöko u kel tölti fel. Ha AL = 0, akkor az egész képerny t felgörgeti, ami végülis a kép törléséhez vezet. o Az ablak bal fels sarkának koordinátáit CX, míg a jobb alsó sarokét DX tartalmazza. A fels o o bájtok (CH és DH) a sort, az alsók (CL és DL) az oszlopot jelentik, a számozás 0-tól kezd dik. o Most úgy tekintjük, hogy a képerny 80 oszlopos és 25 soros képet jelenít meg, ezért DH-ba o 24-et (18h), DL-be pedig 79-et (4Fh) töltünk. A törlés után a kurzor a képerny legalsó sorának o elejére kerül. A GotoXY eljárás a kurzor pozícionálását teszi meg. A 02h számú video-szolgáltatás a DHadik sor DL-edik oszlopába rakja a kurzort, a számozás itt is 0 bázisú. BH-ba 0-t rakunk, de ezzel most ne tör djünk. (Akit érdekel, BH-ban a használt képerny lap sorszámát kell megadni.) o o A f program nagyon egyszer re sikeredett, hiszen a legtöbb dolgot eljáráshívással intézi el. o u A képerny letörlése (CALL Torol) után belépünk a @Ciklus kezdet hurokba. Itt a kurzort o u felrakjuk a bal fels sarokba, ahová kiírjuk a számláló aktuális értékét, majd megnöveljük a o változót. Most jön az, hogy meg kell nézni, nyomtak-e le billenty t. Erre a nemrég látott 00h-s u INT 16 szolgáltatás nem jó, hiszen az nekiáll várakozni, ha nincs lenyomva egyetlen gomb sem. Ehelyett a 01h funkciót használjuk fel, ami a ZF-ben jelzi, volt-e billenty lenyomva: ha ZF = 1, u akkor nem, különben AX tartalma megfelel a 00h-s szolgáltatás által visszaadottnak (tehát ALben az ASCII kód, AH-ban a scan kód). Így ha ZF = 1, akkor visszamegyünk a ciklus elejére. Máskülönben megnézzük, hogy a szóközt nyomták-e meg, ennek ASCII kódja 32 (20h), és ha igen, akkor vége a bulinak, a @Vege címkén át befejezzük a ténykedést. Egyébként a kurzort a következ (második) sorba állítjuk (erre azért jó most az INC DH, mert DX végig nulla marad a o @Ciklus utáni sortól kezdve), majd a megszokott INT 21h 02h számú szolgáltatással kirakjuk a lenyomott billenty ASCII kódjának megfelel karaktert, ami AL-ben van. Az utána következ u o o két sor (XOR AH,AH // INT 16h) feleslegesnek t nhet, de enélkül a következ lenyomott karaku o tert nem fogja érzékelni a program. (Kicsit precízebben: Az INT 16h egy pufferb l olvassa ki a o következ lenyomott gomb kódjait. A 01h-s szolgáltatás a puffer mutatóját nem állítja át, így a o következ hívás ismét ezt a billenty t jelezné lenyomottnak, ami nem lenne okés. A 00h hívása o u viszont módosítja a mutatót, és ezzel minden rendben lesz.) Dolgunk végeztével visszatérünk a ciklusba.

14.3. Munka állományokkal
Az állomány-kezelés nem tartozik a könny dolgok közé, de egyszer érdemes vele foglalu kozni, sokszor ugyanis egyszer bben célhoz érhetünk Assemblyben, mint valamelyik magas u szint nyelvben. u A feladat nem túl bonyolult: hozzunk létre egy állományt, majd figyeljük a billenty zetet. u Minden beírt karaktert írjon ki a program a képerny re és az állományba is folyamatosan és o azonnal. Az Esc megnyomására zárja le az állományt és lépjen ki a programból. Az Esc-hez tartozó karaktert már nem kell az állományba írni. Az állomány nevét a programban konstans módon tároljuk, legyen mondjuk TESZT.OUT. Ha nem sikerült létrehozni az állományt, akkor írjon ki egy hibaüzenetet, majd azonnal fejezze be a m ködést. u
Pelda13.ASM:
MODEL SMALL

14.3. MUNKA ÁLLOMÁNYOKKAL

97

.STACK ADAT FNev Hiba Karakter ADAT KOD SEGMENT DB DB DB DB ENDS

"TESZT.OUT",00h "Hiba a fájl " "létrehozásakor!$" ?

SEGMENT ASSUME CS:KOD,DS:ADAT

@Start: MOV MOV MOV XOR LEA INT JC MOV MOV @Ciklus: XOR INT CMP JE MOV MOV MOV INT MOV LEA INT JMP @Lezar: MOV INT JMP @Hiba: MOV LEA INT @Vege: MOV INT ENDS END AX,4C00h 21h @Start AH,09h DX,[Hiba] 21h AH,3Eh 21h @Vege AH,AH 16h AL,1Bh @Lezar [Karakter],AL AH,02h DL,AL 21h AH,40h DX,[Karakter] 21h @Ciklus AX,ADAT DS,AX AH,3Ch CX,CX DX,[FNev] 21h @Hiba BX,AX CX,0001h

KOD

Ha állományokkal, könyvtárakkal vagy lemezekkel kell dolgoznunk, azt mindenképpen a DOS szolgáltatásain keresztül érdemes tenni, hacsak valami egyéb indok (pl. a sebesség kriti-

14.3. MUNKA ÁLLOMÁNYOKKAL

98

kus) nem indokol mást. A DOS az összes megszokott tevékenység végrehajtását lehet vé teszi o az INT 21h megszakítás szolgáltatásain keresztül, amint ezt a 14.2. táblázat mutatja. 14.2. táblázat. Állomány- és lemezkezel DOS-szolgáltatások o 39h 3Ah 3Bh 3Ch 3Dh 3Eh 3Fh 40h 41h 42h 43h Könyvtár létrehozás (MKDIR) Könyvtár törlés (RMDIR) Könyvtárváltás (CHDIR) Állomány létrehozás/csonkítás (Create) Állomány megnyitás (Open) Állomány lezárás (Close) Olvasás megnyitott állományból (Read) Írás megnyitott állományba (Write) Lezárt állomány törlése (Delete/Unlink) Fájlmutató pozícionálása (Seek) Attribútumok beállítása/olvasása (CHMOD)

Ezeknek az állománykezel függvényeknek közös tulajdonsága, hogy az állományra a mego nyitás és/vagy létrehozás után nem az állomány nevével, hanem egy speciális, egyedi azonosítószámmal, az ú.n. file handle-lel hivatkoznak. Ez egy 16 bites érték. A szabványos ki-/bemeneti eszközök (standard input és output) a 0000h ­ 0004h értékeken érhet k el, ezekre is ugyanúgy o írhatunk, ill. olvashatunk róluk, mintha közönséges fájlok lennének. A 14.3. táblázat mutatja az ilyen eszközök handle számát. 14.3. táblázat. Standard I/O eszközök handle értéke 0 1 2 3 4 Standard input (STDIN) Standard output (STDOUT) Standard hiba (STDERR) Standard soros porti eszköz (STDAUX) Standard nyomtató (STDPRN)

Új állomány létrehozására a 3Ch számú szolgáltatás való. Hívásakor CX-ben kell megadni a leend állomány attribútumait a következ módon: az alsó három bit egyes bitjei kapcsolóknak o o felelnek meg, 1 jelenti a csak írható (Read Only) hozzáférést, 2 a rejtett fájlt (Hidden), 4 pedig a rendszerfájlt (System). Ezek különböz kombinációja határozza meg a végleges attribútuo mot. Most nekünk egy normál attribútumú fájlra van szükségünk, így CX nulla lesz. DS:DX tartalmazza az állomány nevének címét, amely név egy ASCIIZ sztring, azaz a 00h kódú karakterrel kell lezárni. A szolgáltatás visszatéréskor CF-ben jelzi, hogy volt-e hiba. Ha CF = 1, akkor volt, ilyenkor AX tartalmazza a hiba jellegét leíró hibakódot, különben pedig AX-ben a file handle található. Hiba esetén kiírjuk a hibaüzenetet a 09h-s szolgáltatással, majd befejezzük a programot. Állomány létrehozásánál figyelni kell arra, hogy ha az adott nev állomány u már létezik, akkor a DOS nem tér vissza hibával, hanem az állomány méretét 0-ra állítja, majd megnyitja azt. Ha a létrehozás sikerült, akkor a handle-t átrakjuk BX-be, mivel a további szolgáltatások már ott fogják keresni. A billenty zetr l való olvasásra a már említett 00h-s funkciót használjuk. Az Esc billenty u o u a 17 (1Bh) kódú karaktert generálja, így ha ezt kaptuk meg AL-ben, akkor elugrunk a @Lezar címkére. Megnyitott (ez fontos!) állomány lezárására a 3Eh szolgáltatást kell használni, ami

14.4. GRAFIKUS FUNKCIÓK HASZNÁLATA

99

BX-ben várja a handle-t, visszatéréskor pedig CF ill. AX mutatja az esetleges hibát és annak

okát. Ha nem az Esc-et nyomták le, akkor AL-t berakjuk a Karakter nev változóba, majd kiíratjuk u a képerny re a már jól ismert 02h DOS-funkcióval. Ezután a 40h számú szolgáltatást vesszük o igénybe a fájlba íráshoz. A handle-t itt is BX-ben kell közölni, CX tartalmazza a kiírandó bájtok számát (ez most 1 lesz), DS:DX pedig annak a memóriaterületnek a címe, ahonnan a kiírást kérjük. Visszatéréskor AX a ténylegesen kiírt bájtok számát mutatja, ill. a hibakódot, ha CF = 1. Ezek után visszatérünk a @Ciklus címkére.

14.4. Grafikus funkciók használata
Most ismét egy kicsit nehezebb feladatot oldunk meg, de a fáradozás meg fogja érni. A program el ször átvált a 640 · 480 felbontású grafikus videomódba, majd egy pattogó fehér o pontot jelenít meg. A pont mindegyik falon (a képerny szélein) rendesen vissza fog pattanni. o Billenty lenyomására pedig vissza fogja állítani a képerny módot szövegesre, majd ki fog u o lépni.
Pelda14.ASM:
MODEL SMALL .STACK KOD SEGMENT ASSUME CS:KOD,DS:NOTHING

@Start: MOV INT XOR MOV MOV MOV XOR @Ciklus: MOV INT ADD JNS NEG ADD @V1: CMP JB NEG ADD @V2: ADD JNS NEG DX,DI @F1 DI CX,640 @V2 SI CX,SI AX,0C00h 10h CX,SI @V1 SI CX,SI AX,0012h 10h CX,CX DX,CX SI,3 DI,2 BH,BH

14.4. GRAFIKUS FUNKCIÓK HASZNÁLATA

100

ADD @F1: CMP JB NEG ADD @F2: MOV INT PUSH MOV @Var1: XOR @Var2: LOOP DEC JNZ POP MOV INT JZ XOR INT MOV INT MOV INT ENDS END

DX,DI DX,480 @F2 DI DX,DI AL,0Fh 10h ;Várakozás . . . CX DX DL,10 CX,CX @Var2 DL @Var1 DX CX ; AH,01h 16h @Ciklus AH,AH 16h AX,0003h 10h AX,4C00h 21h @Start

KOD

Adatszegmensre most nincs szükségünk, úgyhogy rögtön a videomód beállításával kezdünk. Az INT 10h 00h-s szolgáltatásával állítható be a képerny n használt videomód, az igényelt mód o számát AL-ben kell közölnünk. Sok szabványos videomódnak van el re rögzített száma, de o nekünk most csak a 80 · 25 felbontású színes szöveges (ez a 03h számú mód), ill. a 640 · 480 felbontású grafikus (12h a száma) VGA-módokra lesz szükségünk. A VGA (Video Graphics Array) a videovezérl típusára utal, azaz ennél régebbi típusú (EGA, CGA, HGC stb.) videoo kártyával nem tudjuk kipróbálni a programot, de ez manapság már nem túl nagy akadály. A pattogó pont koordinátáit a DX és CX regiszter tárolja majd, DX mutatja a sort, CX pedig az oszlopot. Mindkett 0 bázisú, az origó tehát a (0,0). BH a használt képerny lap sorszámát o o tartalmazza, de ez most lényegtelen. SI lesz a vízszintes, DI pedig a függ leges lépésköz, azaz o ennyi képponttal fog arrébb ugrani a pont. A f ciklusban el ször törölni kell a pont el z képét, majd ki kell számolni az új koordináo o o o tákat, ki kell rakni az új helyére a pontot, végül ellen rizni kell a billenty zetet. o u A pont törlése annyit jelent, hogy a legutóbbi (CX,DX) koordinátákra fekete színnel kirakunk egy pontot. Pont rajzolására a 0Ch szolgáltatás lesz jó. AL-ben a rajzolt pont színét kell megadni, ehhez tekintsük a 14.4. táblázatot. Látjuk, hogy a fekete kódja a 00h. BH-ban a már említett képerny lap-sorszám van, a kirakandó pont koordinátáit pedig éppen a CX, DX regiszo terekben várja. Miután töröltük a pont el z példányát, ideje, hogy az új hely koordinátáit meghatározzuk. o o

14.4. GRAFIKUS FUNKCIÓK HASZNÁLATA

101

14.4. táblázat. Színkódok 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 0Ah 0Bh 0Ch 0Dh 0Eh 0Fh Fekete Kék Zöld Ciánkék Vörös Bíborlila Barna Világosszürke Sötétszürke Világoskék Világoszöld Világoscián Világospiros Világosbíbor Sárga Fehér

A vízszintes és függ leges koordinátákat hasonlóan dolgozzuk fel, ezért csak az egyiket nézzük o most meg. Tekintsük mondjuk az abszcissza kiszámítását. El ször természetesen a lépésközt o (SI) adjuk hozzá CX-hez. Ha a kapott érték negatív, akkor SF be fog állni. Eszerint végrehajtjuk a szükséges korrekciót és rátérünk a @V1 címkére, vagy pedig rögtön erre a címkére jövünk. A korrekció abban áll, hogy a lépésköz el jelét megfordítjuk (tehát negáljuk a lépésközt), majd o ezt az új lépésközt ismét hozzáadjuk CX-hez. Ha ezen az ellen rzésen túljutottunk, akkor még o meg kell nézni, hogy a képerny jobb oldalán nem mentünk-e túl. Ha igen (azaz CX > 639), o akkor ugyanúgy járunk el, mint az el bb, tehát negáljuk a lépésközt és hozzáadjuk CX-hez. DX o módosítása teljesen hasonló módon történik. Most már kirakhatjuk a pontot új helyére. Mivel fehér pontot szeretnénk, AL-be 0Fh-t rakunk. Az ez után következ néhány sor nem tartozik az eredeti célkit zéshez, de a m ködés elo u u len rzéséhez elengedhetetlen. A két pontosvessz vel közrefogott sorok csak a ciklus lassítását o o szolgálják, és nem csinálnak mást, mint 655360-szor végrehajtják az üres ciklusmagot. Erre a mostani számítógépek gyorsasága miatt van szükség, különben a pont nemhogy pattogna, de valósággal száguldana a képerny n. o Miután letelt a késleltetés, a 01h-s INT 16h szolgáltatás hívásával megnézzük, hogy nyomtake le billenty t, s ha nem, akkor irány vissza a ciklus elejére. u Kilépés el tt még egyszer kiolvassuk a billenty zetet, majd visszaállítjuk a videomódot o u szövegesre.

15. fejezet

Megszakítás-átdefiniálás, hardver-megszakítások, rezidens program, kapcsolat a perifériákkal, hardver-programozás
Ideje, hogy kicsit mélyebben elmerüljünk a megszakítások világában. Nézzük meg el ször, o hogy is hajtja végre a processzor az INT utasítást. A 0000h szegmens els 1024 bájtján (a 0000h és a 03FFh offszetek közti területen) található o a megszakítás-vektor tábla. Ez nem más, mint egy ugrótábla, mivel mindegyik bejegyzése egy 4 bájtos távoli pointer (azaz szegmens:offszet alakú memóriacím), nevük megszakítás-vektor (interrupt vector). Kicsit utánaszámolva láthatjuk, hogy 256 · 4 = 1024. Ebben a táblában található tehát mind a 256 db. szoftver- (részben hardver- is) megszakítás végrehajtó programjának kezd címe. Ezeket a programokat megszakítás-kezel nek (interrupt handler) nevezzük. o o Minden megszakításnak pontosan egy kezel programja van, de ugyanaz a kezel több megszao o kításhoz is tartozhat. Ha a programban az INT utasítás kódjával találkozik a processzor (ami 0CDh), akkor kiolvassa az utána lev bájtot is, ez a kért megszakítás száma. Felismerve, hogy megszakítás o következik, a verembe berakja sorban a Flags, CS és IP regiszterek aktuális értékét (CS:IP az INT utasítást követ utasítás címét tartalmazza), majd az IF és TF flag-eket törli, ezzel bizo tosítva, hogy a megszakítás lefolyását nem fogja semmi megakadályozni. (Szimbolikusan: PUSHF // PUSH CS // PUSH IP) Jelölje a kért megszakítás számát N. Ezek után a processzor betölti a CS:IP regiszterpárba a memória 0000h:(N · 4) címén lev duplaszót, azaz CS-be a o 0000h:(N· 4 + 2) címen lev , míg IP-be a 0000h:(N· 4) címen található szó kerül betöltésre (emo lékezzünk vissza, a processzor little-endian tárolásmódot használ). Ennek hatására az N-edik megszakítás-vektor által mutatott címen folytatódik a végrehajtás. Ha a kezel elvégezte dolgát, akkor egy különleges utasítás segítségével visszatér az ot hívó o programhoz. Erre az IRET (Interrupt RETurn) operandus nélküli utasítás szolgál. Az IRET kiadásakor a processzor a visszatérési címet betölti a CS:IP-be (azaz leemeli el ször az IPo t, majd CS-t a veremb l), a Flags regiszter tartalmát szintén visszaállítja a veremb l, majd a o o végrehajtást az új címen folytatja. (Szimbolikusan: POP IP // POP CS // POPF) Nézzünk egy példát! Tegyük fel, hogy az INT 21h utasítást adjuk ki, és legyen Flags =

15.1. SZOFTVER-MEGSZAKÍTÁS ÁTIRÁNYÍTÁSA

103

0F283h (azaz IF = SF = CF = 1, a többi flag mind 0), CS = 7000h, IP = 1000h, SS lényegtelen, SP = 0400h. Az INT 21h megszakítás vektora a 0000h:0084h címen található, ennek értéke most legyen mondjuk 2000h:3000h. Az INT 21h utasítás végrehajtásának hatására a következ k történnek: CS:IP értéke 2000h:3000h lesz, Flags-ben törl dik az IF flag, így az a o o 0F083h értéket fogja tartalmazni, valamint a verem a következ képet fogja mutatni: o
SS:0400h : ?? SS:03FEh : 0F283h (Flags) SS:03FCh : 7000h (CS) SS:03FAh : 1000h (IP) SS:03F8h : ??

SP

Mivel tudjuk, hogy hol találhatók az egyes megszakítások belépési pontjai, megtehetjük, hogy bármelyik vektort kedvünkre átírjuk. Ezzel a lehet séggel rengeteg program él is, elég, ha o a DOS-t, BIOS-t, vagy mondjuk az egeret kezel programot (eszközmeghajtót) említjük. És mio vel a hardver-megszakítások is bizonyos szoftver-megszakításokon keresztül lesznek lekezelve, akár ezeket is átirányíthatjuk saját programunkra. Mindkét esetre mutatunk most példákat.

15.1. Szoftver-megszakítás átirányítása
Els programunk a következ t fogja tenni: indításkor át fogja venni az INT 21h kezelését. o o Az új kezel a régit fogja meghívni, de ha az igényelt funkció a 09h-s lesz (az a bizonyos o sztringkiíró rutin), akkor DS:DX-et egy el re rögzített szöveg címére fogja beállítani, és arra o fogja meghívni az eredeti szövegkiíró funkciót. A program billenty lenyomása után vissza u fogja állítani az eredeti megszakítás-kezel t, majd be fog fejez dni. o o
Pelda15.ASM:
MODEL SMALL .STACK ADAT Helyes Teszt ADAT KOD SEGMENT DB "Ha ezt látod, akkor" DB " muködik a dolog.$" DB "Ez nem fog megjelenni!$" ENDS SEGMENT ASSUME CS:KOD,DS:ADAT DW PROC PUSHF CMP JNE POPF PUSH PUSH ?,?

RegiCim UjKezelo

AH,09h @Nem09h DS DX

15.1. SZOFTVER-MEGSZAKÍTÁS ÁTIRÁNYÍTÁSA

104

MOV MOV LEA PUSHF CALL POP POP IRET @Nem09h: POPF JMP ENDP

DX,ADAT DS,DX DX,[Helyes] DWORD PTR CS:[RegiCim] DX DS

DWORD PTR CS:[RegiCim]

UjKezelo @Start:

KOD

MOV MOV XOR MOV CLI LEA XCHG MOV MOV XCHG MOV STI MOV LEA INT MOV MOV INT MOV INT XOR INT CLI MOV MOV MOV MOV STI MOV INT ENDS END

AX,ADAT DS,AX AX,AX ES,AX AX,[UjKezelo] AX,ES:[21h*4] CS:[RegiCim],AX AX,CS AX,ES:[21h*4+2] CS:[RegiCim+2],AX AH,09h DX,[Teszt] 21h AH,02h DL,0Dh 21h DL,0Ah 21h AH,AH 16h AX,CS:[RegiCim] ES:[21h*4],AX AX,CS:[RegiCim+2] ES:[21h*4+2],AX AX,4C00h 21h @Start

Az els szembet n dolog az, hogy a kódszegmensben van definiálva a RegiCim nev o u o u változó, ami majd az eredeti INT 21h kiszolgáló címét fogja tárolni. Ennek magyarázata a megszakítás-kezel rutin m ködésében rejlik. o u Azt már említettük, hogy minden eljárás elején érdemes és illend elmenteni a használt o regisztereket a verembe, hogy azokat az eljárás végén gond nélkül visszaállíthassuk eredeti

15.1. SZOFTVER-MEGSZAKÍTÁS ÁTIRÁNYÍTÁSA

105

értékükre. Ez a szabály a megszakítás-kezel rutinokra kötelez re változik. A regiszterekbe o o a szegmensregisztereket és a Flags regisztert is bele kell érteni. Ismétlésként, az operandus nélküli PUSHF utasítás a verembe berakja a Flags regisztert, míg a POPF a verem tetején lev o szót a Flags-be tölti be. A saját kezel rutinunk m ködése két ágra fog szakadni aszerint, hogy AH egyenl -e 09h-val o u o avagy nem. A feltétel tesztelését a hagyományos CMP-JNE párossal oldjuk meg, de mivel ez az utasítás megváltoztat(hat) néhány flag-et, ezért a tesztelés el tt PUSHF áll, valamint mindkét ág o a POPF-fel indul. Ez garantálja, hogy az összes flag érintetlenül marad. Erre kérdezhetné va laki, hogy miért orizzük meg a flag-ek értékét, ha a Flags úgyis el van mentve a veremben. Nos, már láttuk, hogy néhány szolgáltatás pl. a CF-ben jelzi, hogy volt-e hiba. Ezek a szolgáltatások visszatérés során a veremben lev Flags regiszter-példányt egyszer en eldobják (kés bb látni o u o fogjuk, hogyan), így módosításunk a hívó programra is visszahatna, ami kellemetlen lehetne. Ha AH 09h, akkor a @Nem09h talányos nev címkén folytatódik a megszakítás kiszolu gálása. Azt mondtuk, hogy minden egyéb esetben a régi funkciót fogjuk végrehajtatni. Ennek megfelel en cselekszik a programban szerepl JMP utasítás. Ezt a mnemonikot már ismerjük és o o használtuk is. Itt viszont két újdonságot is láthatunk: az els , hogy az operandus nem egy eljáo rás neve vagy címkéje mint eddig, hanem egy memóriahivatkozás (szegmensprefixszel együtt). Az ugrás eme formáját közvetett avagy indirekt ugrásnak (indirect jump) nevezzük, míg a régebbi alakot közvetlen vagy direkt ugrásnak (direct jump). Az elnevezés arra utal, hogy a cél címét nem az operandus, hanem az operandus által mutatott memóriacímen lev változóban o lev pointer határozza meg. Magyarán: a JMP-t úgy hajtja végre a processzor, hogy kiszámítja o az operandus (esetünkben a CS:RegiCim változó) tényleges címét, majd az azon a címen lev o pointert tölti be a megfelel regiszterekbe (IP-be vagy a CS:IP párba). A másik újdonságot o a DWORD PTR képviseli, ennek hatására az operandus típusa most DWORD lesz, ami duplaszót jelent ugyebár. Ha helyette WORD PTR állna, akkor a JMP a szokásos szegmensen belüli avagy közeli ugrást (intrasegment vagy near jump) tenné meg, azaz csak IP-t változtatná meg. Nekünk viszont CS:IP-t kell új értékekkel feltöltenünk, és ez már szegmensközi avagy távoli ugrást jelent (intersegment vagy far jump). A DWORD helyett állhatna még a FAR is, az ugyancsak távoli pointert írna el . Az utasítás most tehát CS-be a CS:(RegiCim + 2) címen o lev , míg IP-be a CS:RegiCim címen lev szót tölti be, majd onnan folytatja a végrehajtást. A o o szemfülesebbek rögtön keresni kezdik az említett IRET-et. Erre most nekünk nincs szükségünk, hiszen SP a régi (híváskori) állapotában van, a Flags is érintetlen, és az eredeti kiszolgálóból való visszatérés után nem akarunk már semmit sem csinálni. Ezért a legegyszer bb módszert u választjuk: a feltétlen vezérlésátadást a régi rutinra. Ha az a rutin befejezte ténykedését, a veremben az eredeti Flags, CS és IP értékeket fogja találni, és így közvetlenül a hívó programba (nem az UjKezelo eljárásba) fog visszatérni. Ha AH = 09h, akkor el kell végeznünk DS:DX módosítását tervünknek megfelel en. Mivel o nem szép dolog, ha a változás visszahat a hívóra, mindkét regisztert elmentjük a verembe. Ez a m velet azonban meggátol bennünket abban, hogy a másik esethez hasonlóan ráugorjunk a u régi kezel címére. Ha ugyanis a JMP DWORD PTR CS:[RegiCim] utasítást alkalmaznánk o itt is, akkor a hívó programba visszatérés nem igazán sikerülne, mondhatni cs döt mondana. o Hogy miért? A kulcs a verem. A legutolsó két veremm velettel a verem tetején a DS és DX u regiszterek híváskori értéke lesz, amit a régi kezel rutin a CS:IP regiszterekbe fog betölteni, és o ez valószín leg katasztrofális lesz (nem beszélve arról, hogy SP is meg fog változni a hívásu kori helyzethez képest). Ezért most ugrás helyett hívást kell alkalmaznunk. Ez a hívás is kicsit más, mint az eddig megismert. A CALL utasítás ilyen alakját a fenti példához hasonlóan távoli indirekt eljáráshívásnak nevezzük. (A CALL-nak is létezik közeli és távoli alakja, és mindkett b l van direkt és indirekt változat is.) A CALL hatására CS és IP bekerül a verembe, a Flags o o

15.1. SZOFTVER-MEGSZAKÍTÁS ÁTIRÁNYÍTÁSA

106

viszont nem, miközben a régi kiszolgálórutin arra számít, hogy az SS:(SP + 4) címen (ez nem szabályos címzésmód!) a Flags tükörképe van. Ezért a CALL el tt még kiadunk egy PUSHF-et. o Aki kicsit jobban elgondolkodik, annak felt nhet, hogy ez az utasításpáros tulajdonképpen egy u INT utasítást szimulál. Így ha a régi kiszolgáló végez, akkor visszatér a saját kezel nkbe, ahol o mi kitakarítjuk a veremb l a DS és DX értékeit, majd visszatérünk a hívó programba. Ezt most o IRET-tel tesszük meg annak ellenére, hogy említettük, némelyik szolgáltatás esetleg valamelyik flag-ben adhatna vissza eredményt. Mi most viszont csak a 09h számú szolgáltatás m ködésébe u avatkozunk be, ami nem módosítja egyik flag-et sem. Ha egészen korrekt megoldást akarunk, akkor az IRET utasítást a RETF 0002h utasítással kell helyettesíteni. A RETF (Far RETurn) az eddig használt RET utasítástól abban tér el, hogy visszatéréskor nemcsak IP-t, de CS-t is visszaállítja a veremb l (tehát m ködése szimbolikuo u san POP IP // POP CS). Az opcionális szó méret közvetlen operandus azt az értéket jelöli, u amit azután (t.i. IP és CS POP-olása után) SP-hez hozzá kell adnia. A visszatérés eme formája annyiban tér el az IRET-t l, hogy a Flags eredeti értékét nem állítja vissza a veremb l, a mego o adott 0002h érték hatására viszont SP-t úgy módosítja, mintha egy POPF-et is végrehajtottunk volna. Az eredmény: a hívóhoz gond nélkül visszatérhetünk a megszakításból úgy, hogy esetleg valamelyik flag eredményt tartalmaz. Nem válaszoltuk még meg, hogy miért a kódszegmensben definiáltuk a RegiCim változót. A válasz sejthet : ha az adatszegmensben lenne, akkor annak eléréséhez el ször be kellene o o állítanunk DS-t. Ez viszont ellentmond annak a követelménynek, hogy DS értékét nem (sem) szabad megváltoztatnunk abban az esetben, ha AH 09h. Ezt csak valamilyen csellel tudnánk biztosítani, pl. így:
PUSH MOV MOV PUSH PUSH POP RETF DS DX DX,ADAT DS,DX WORD PTR DS:[RegiCim+2] WORD PTR DS:[RegiCim] DX DS

Hangsúlyozzuk, ezt csak akkor kellene így csinálni, ha az ADAT nev szegmensben definiáltuk u volna a RegiCim változót. Mivel azonban a kódszegmensbe raktuk a definíciót, a kezel rutin o közvetlenül el tudja érni a változót. (Ehhez azért az is kell, hogy az INT 21h kiadása után a saját kezel rutinunk CS szegmense megegyezzen a RegiCim szegmensével, de ez most fennáll.) o ES-t eddig még nem sokszor használtuk, most viszont kapóra jön a megszakítás-vektor táblázat szegmensének tárolásánál. Miel tt nekifognánk átírni az INT 21h vektorát, a CLI utasítással letiltjuk a hardver-megszakításokat o (IF = 0 lesz). Erre azért van szükség, mert ha az átírás közepén bejönne egy megszakítás, akkor azt a processzor minden további nélkül kiszolgálná. Ha annak kezel jében szerepelne egy o INT 21h utasítás, akkor a rossz belépési cím miatt nem a megfelel rutin hívódna meg, és ez o valószín leg álomba küldené a gépet. A vektor módosítása után ismét engedélyezzük a bejöv u o megszakításokat az IF 1-be állításával, amit az STI utasítás végez el. A vektor módosításánál az XCHG egy csapásra megoldja mind a régi érték kiolvasását, mind az új érték beírását. Miután sikeresen magunkra irányítottuk ezt a megszakítást, rögtön ki is próbáljuk. A helyes m ködés abban áll, hogy nem a Teszt címkéj szöveg fog megjelenni, hanem a Helyes. Annak u u ellen rzésére, hogy a többi szolgáltatást nem bántottuk, a 02h funkcióval egy új sor karakterpárt o

15.2. AZ IDOZÍTO (TIMER) PROGRAMOZÁSA

107

írunk ki. Végül gombnyomás után visszaállítjuk az eredeti kezel t, és kilépünk a programból. o

15.2. Az id zít (timer) programozása o o
A következ program már a hardver-programozás tárgykörébe tartozik. A feladat az, hogy o írjunk egy olyan eljárást, ami ezredmásodperc (millisecundum) pontossággal képes meghatározott ideig várakozni. Ezzel például stoppert is megvalósíthatunk. Ez a rész kicsit magasabb szint a szokásosnál, így ha valaki esetleg nem értené, akkor nyugodtan ugorja át. :) u
Pelda16.ASM:
MODEL SMALL .STACK KOD SEGMENT ASSUME CS:KOD,DS:NOTHING DW DW DW DW PROC PUSH INC MOV ADD JC MOV OUT POP IRET POP JMP ENDP ? ? ? ?,?

Orajel Oszto MSec RegiCim UjKezelo

AX CS:[MSec] AX,CS:[Oszto] CS:[Orajel],AX @Regi AL,20h 20h,AL AX

@Regi: AX DWORD PTR CS:[RegiCim]

UjKezelo @Start:

XOR MOV CLI LEA XCHG MOV MOV XCHG MOV MOV MOV

AX,AX DS,AX AX,[UjKezelo] AX,DS:[08h*4] CS:[RegiCim],AX AX,CS AX,DS:[08h*4+2] CS:[RegiCim+2],AX DX,0012h AX,34DCh

15.2. AZ IDOZÍTO (TIMER) PROGRAMOZÁSA

108
BX,1000 BX CS:[Oszto],AX CS:[Orajel],0000h BL,AL AL,36h 43h,AL $+2 AL,BL DX,0040h DX,AL $+2 AL,AH DX,AL $+2 AH,02h DL,'1' CX,9 BX,1000 21h DL CS:[MSec],0000h CS:[MSec],BX @Var @Ciklus DL,0Dh 21h DL,0Ah 21h AX,CS:[RegiCim] DS:[08h*4],AX AX,CS:[RegiCim+2] DS:[08h*4+2],AX AL,36h 43h,AL $+2 AL,AL 40h,AL $+2 40h,AL $+2 AX,4C00h 21h @Start

MOV DIV MOV MOV MOV MOV OUT JMP MOV MOV OUT JMP MOV OUT JMP STI MOV MOV MOV MOV @Ciklus: INT INC MOV @Var: CMP JB LOOP MOV INT MOV INT CLI MOV MOV MOV MOV MOV OUT JMP XOR OUT JMP OUT JMP STI MOV INT ENDS END

KOD

Adatszegmensre nem lesz szükségünk, a változókat a könnyebb elérhet ség miatt a kódo

15.2. AZ IDOZÍTO (TIMER) PROGRAMOZÁSA

109

szegmensben definiáljuk. A PC hardverének áttekintésekor már megemlítettük az id zít t (timer). Ez az egység háo o rom független számlálóval (counter) rendelkezik (szemléletesen három stopperrel), és mindegyik egy id zít -csatornához tartozik. Ebb l egyik a már szintén említett memória-frissítéshez o o o kell, egy pedig a bels hangszóróra van rákötve. A harmadik felel s a bels rendszeróra karo o o bantartásáért, valamint a floppy meghajtó motorjának kikapcsolásáért. Bár els re nem látszik, a o megoldást ez utóbbi fogja jelenteni. Ha ez a számláló lejár, az id zít egy megszakítást kér, ez o o a megszakítás-vezérl IRQ0-ás vonalán keresztül valósul meg. Végül a hardver-megszakítást a o processzor érzékeli, és ha lehet, kiszolgálja. Nos, ha az eredeti kiszolgálót mi lecseréljük a saját rutinunkra, ami a speciális feladatok elvégzése után meghívja a régi kiszolgálót, akkor nyert ügyünk van. Az id zít egy kvarckristályon keresztül állandó frekvenciával kapja az áramimpulzusokat. o o Ez a frekvencia az 12 34DCh Hz (1193180 Hz). Az id zít mindhárom számlálójához tartoo o zik egy 16 bites osztóérték. A dolog úgy m ködik, hogy az adott számláló másodpercenként u 12 34DCh/Osztó-szor jár le. A rendszerórához tartozó számláló (ez a 0-ás csatorna) osztója 65536, míg a memória-frissítésért felel s (ez az 1-es csatorna) számlálóé 18 alapállapotban. Ha o elvégezzük az osztásokat, azt látjuk, hogy a memóriát másodpercenként kb. 66288-szor frissítik, míg a rendszerórát másodpercenként kb. 18.2-szer állítja át a kiszolgálórutin. Tervünket úgy fogjuk megvalósítani, hogy az id zít 0-ás csatornájának osztóját olyan éro o tékre állítjuk be, hogy másodpercenként 1000-szer generáljon megszakítást. A megszakítást az IRQ0 vonalról a megszakítás-vezérl átirányítja az INT 08h szoftver-megszakításra, de ett l ez o o még hardver-megszakítás marad, aminek fontosságát kés bb látjuk majd. Ha IF = 1, a proo cesszor érzékeli a megszakítást, majd meghívja az INT 08h kezel jét, ami elvégzi a teend ket. o o Ezt a kezel t cseréljük le egy saját rutinra, ennek neve UjKezelo lesz. o Nézzük meg el ször a változók szerepét. Az eredeti kezel címét ismét a RegiCim táo o rolja. Oszto tartalmazza a számlálóhoz tartozó kiszámolt osztóértéket. MSec tartalma minden megszakítás-kéréskor eggyel fog n ni, ezt használjuk fel majd a pontos várakozás megvalósítáo sára. Az Orajel változó szerepének megértéséhez szükséges egy dolgot tisztázni. Mikor is kell meghívni a régi kiszolgálót? Ha minden megszakítás esetén meghívnánk, akkor az óra az eredetinél sokkal gyorsabban járna, és ezt nem szeretnénk. Pontosan úgy kell m ködnie, mint el tte. u o Ehhez nekünk is üzemeltetnünk kell egy számlálót. Ha ez lejár, akkor kell meghívni a régi kiszolgálót. Számláló helyett most osztót fogunk bevezetni, ennek mikéntje mindjárt kiderül. Az UjKezelo eljárásban csak az AX regisztert használjuk fel, ezt tehát rendesen elmentjük a verembe. Ezt követ en megnöveljük eggyel az MSec értékét célunk elérése érdekében. Most o következik annak eldöntése, meg kell-e hívni a régi kiszolgálót. Ehhez az osztóértéket (Oszto tartalmát) hozzáadjuk az Orajel változóhoz. Ha átvitel keletkezik, az azt jelenti, hogy Orajel értéke nagyobb vagy egyenl lett volna 65536-nál. Mivel azt mondtuk, hogy Orajel egy osztó o szerepét játssza, innen következik, hogy a régi kezel rutint pontosan akkor kell meghívni, amio kor CF = 1 lesz. Gyakorlatilag az id zít m ködését utánozzuk: ha az osztóértékek összege o o u eléri vagy meghaladja a 65536-ot, és ekkor hívjuk meg a régi rutint, ez pontosan azt csinálja, mint amikor az id zít az eredeti 65536-os osztó szerinti id közönként vált ki megszakítást. o o o Némi számolással és gondolkozással ezt magunk is beláthatjuk. Ha CF = 1, akkor szépen ráugrunk a régi kiszolgáló címére, s az majd dolga végeztével visszatér a megszakításból. Ha viszont CF = 0, akkor nekünk magunknak kell kiadni a parancsot a visszatéréshez. El tte azonban meg kell tenni egy fontos dolgot, és itt lesz szerepe annak a ténynek, hogy ez o a rutin mégiscsak hardver-megszakítás miatt hajtódik végre. Dolgunk végeztével jelezni kell a megszakítás-vezérl nek, hogy minden oké, lekezeltük az adott hardver-megszakítást. Ha ezt o

15.2. AZ IDOZÍTO (TIMER) PROGRAMOZÁSA

110

a nyugtázásnak (acknowledgement) nevezett m veletet elfelejtjük megtenni, akkor az esetu legesen beérkez többi megszakítást nem fogja továbbítani a processzor felé a vezérl . A o o megszakítás-vezérl t a 0020h és 0021h számú portokon keresztül érhetjük el (AT-k esetében o a második vezérl a 00A0h és 00A1h portokon csücsül). Az OUT (OUTput to port) utasítás o a céloperandus által megadott portra kiírja a forrásoperandus tartalmát. A port számát vagy 8 bites közvetlen adatként (0000h ­ 00FFh portok esetén), vagy a DX regiszterben kell közölni, míg a forrás csak AL vagy AX lehet. Ha a forrás 16 bites, akkor az alsó bájt a megadott portra, míg a fels bájt a megadott után következ portra lesz kiírva. A nyugtázás csak annyit jelent, o o hogy a 0020h számú portra ki kell írni a 20h értéket (könny megjegyezni:). Ezután IRET-tel u befejezzük ténykedésünket. Megjegyezzük, hogy az OUT párja az IN (INput from port) utasítás. M ködése: a forrásou perandus által megadott portról beolvasott adatot a céloperandusába írja. A portszámot szintén vagy bájt méret közvetlen adatként, vagy a DX regiszterrel kell megadni, célként pedig ismét u AL-t vagy AX-et írhatunk. Ha a cél 16 bites, az alsó bájt a megadott portról, a fels bájt pedig o az utána következ , eggyel nagyobb számúról lesz beolvasva. o Mivel nincs adatszegmens, most DS-t használjuk fel a megszakítás-vektorok szegmensének tárolására. A megszakítások letiltása után a már szokott módon eltároljuk a régi INT 08h kezel o címét, ill. beállítjuk a saját eljárásunkat. Ezt követ en kiszámoljuk az Oszto értékét. Mint o említettük, az id zít alapfrekvenciája 12 34DCh, és minden ezredmásodpercben akarunk majd o o megszakítást, így az el z értéket 1000-rel elosztva a hányados a kívánt értéket fogja adni. Aki o o nem értené, ez miért van így, annak egy kis emlékeztet : o 12 34DCh/osztó = frekvencia Ezt az egyenletet átrendezve már következik a módszer helyessége. Az Orajel változót kinullázzuk a megfelel m ködéshez. Ezután az osztót még az id zít vel is közölni kell, a három OUT o u o o utasítás erre szolgál. Ezekkel most nem foglalkozunk, akit érdekel, az nyugodtan nézzen utána valamelyik említett adatbázisban. A JMP utasítások csak arra szolgálnak, hogy kicsit késleltessék a további bájtok kiküldését a portokra, a perifériák ugyanis lassabban reagálnak, mint a processzor. Megfigyelhetjük a JMP operandusában a cél megjelölésekor a $ szimbólum használatát. Emlékeztet ül: a $ szimbólum az adott szegmensben az aktuális sor offszetjét tartalmazza. o Ez a sor egyéb érdekességet is tartogat, a használt ugrás ugyanis sem nem közeli (near), sem nem távoli (far). Ezt az ugrásfajtát rövid ugrásnak (short jump) nevezzük, és ismertet jele, o hogy a relatív cím a feltételes ugrásokhoz és a LOOP-hoz hasonlóan csak 8 bites. A 2-es szám onnan jön, hogy a rövid ugrás utasításhossza 2 bájt (egy bájt a m veleti kód, egy pedig a relatív u cím). A rövid ugrást még kétféleképpen is kikényszeríthetjük: az egyik, hogy a céloperandus elé odaírjuk a SHORT operátort, a másik, hogy JMP helyett a JMPS (JuMP Short) mnemonikot használjuk. A megszakítás m ködésének tesztelésére egy rövid ciklus szolgál, ami annyit tesz, hogy u kiírja a decimális számjegyeket 1-t l 9-ig, mindegyik jegy után pontosan 1 másodpercet várao kozva. A várakozást az MSec változó figyelésével tesszük annak nullázása után. MSec-et a megszakítások bekövetkeztekor növeli egyesével az UjKezelo eljárás, ez pedig ezredmásodpercenként történik meg. Ha tehát MSec = 1000, akkor a nullázás óta pontosan egy másodperc telt el. A ciklus lejárta után egy új sor karakterpárt írunk még ki, majd végezetül visszaállítjuk az eredeti INT 08h kezel t, valamint az id zít eredeti osztóját. Ezután befejezzük a program o o o m ködését. Az id zít vel kapcsolatban még annyit, hogy a 0000h érték jelöli a 65536-os osztót. u o o A program történetéhez hozzátartozik, hogy az els verzióban az UjKezelo eljárás legutolsó o sorában a DWORD helyén FAR állt. A TASM 4.0-ás verziója azonban furcsa módon nem jól

15.3. REZIDENS PROGRAM KÉSZÍTÉSE

111

fordítja le ezt az utasítást ebben a formában, csak akkor, ha DWORD-öt írunk típusként. Erre kés bbi programjainkban nem árt odafigyelni, ugyanis lehet, hogy a forrás szemantikailag jó, o csak az assembler hibája (tévedése) miatt nem megfelel kód szerepel a futtatható állományban. o Err l általában a debugger segítségével gy z dünk meg. o o o

15.3. Rezidens program (TSR) készítése, a szöveges képerny közvetlen elérése o
Rezidens programon (resident program) egy olyan alkalmazást értünk, amihez tartozó memóriaterületet vagy annak egy részét a DOS nem szabadítja fel a program befejez désekor. A o program kódja ilyenkor a memóriában bentmarad. Az inaktív programot sokszor egy megszakítás vagy valamilyen hardveresemény éleszti fel. A programok másik gyakran használt elnevezése a TSR (Terminate and Stay Resident). Néhány jól ismert segédprogram is valójában egy TSR, mint pl. a DOS CD-meghajtókat kezel interfésze (MSCDEX.EXE), egérmeghajtók o (MOUSE.COM, GMOUSE.COM stb.), különféle képerny lopók, commanderek (Norton Como mander, Volkov Commander, DOS Navigator) stb. TSR-t .COM programként egyszer bb írni, így most mi is ezt tesszük. A feladat az, hogy a u program rezidensen maradjon a memóriában az indítás után, és a képerny bal fels sarkában o o lev karakter kódját és annak színinformációit folyamatosan növelje eggyel. (Csak szöveges o módban!) Nem túl hasznos, de legalább látványos.
Pelda17.ASM:
MODEL TINY KOD SEGMENT ASSUME CS:KOD,DS:KOD ORG 0100h

@Start1: JMP RegiCim UjKezelo DW PROC PUSHF CALL PUSH MOV MOV XOR INC INC INC POP IRET ENDP @Start ?,?

DWORD PTR CS:[RegiCim] DI ES DI,0B800h ES,DI DI,DI BYTE PTR ES:[DI] DI BYTE PTR ES:[DI] ES DI

UjKezelo @Start:

15.3. REZIDENS PROGRAM KÉSZÍTÉSE

112
AX,AX ES,AX SI,ES:[1Ch*4] CS:[RegiCim],SI CS:[RegiCim+2],DS AX,[UjKezelo] ES:[1Ch*4],AX ES:[1Ch*4+2],CS DX,@Start 27h @Start1

KOD

XOR MOV CLI LDS MOV MOV LEA MOV MOV STI LEA INT ENDS END

A feladat megoldásához egy olyan megszakításra kell ,,ráakaszkodni", ami elég sokszor hívódik meg. Ez lehetne az eddig megismertek közül INT 08h, INT 16h, esetleg az INT 21h. Az id zít által kiváltott INT 08h eredeti kezel je dolga végeztével egy INT 1Ch utasítást ad ki, o o o aminek a kezel je alapesetben csak egy IRET-b l áll. Ezt a megszakítást bárki szabadon átio o rányíthatja saját magára, feltéve, hogy meghívja az eredeti (pontosabban a megszakítás-vektor szerinti) kezel t, illetve hogy a megszakításban nem tölt el túl sok id t. Az id korlát betartása o o o azért fontos, mert az INT 08h ugyebár egy hardver-megszakítás, és ha a megszakítás-vezérl t o csak az INT 1Ch lefutása után nyugtázza a kezel , akkor az eltelt id tartam hossza kritikussá o o válhat. Adatszegmensünk most sem lesz, az egy szem RegiCim változót a kódszegmens elején definiáljuk, és mellesleg a rezidens részben foglal helyet. Az UjKezelo eljárás elején hívjuk meg az el z kezel t, s ez után végezzük el a képerny n o o o o a módosításokat. A képerny re nem csak a DOS (INT 21h) vagy a video BIOS (INT 10h) segítségével lehet o írni. A megjelenítend képerny tartalmat a videovezérl kártyán lev videomemóriában tárolo o o o ják el, majd annak tartalmát kiolvasva készül el a végleges kép a monitoron. Ennek a memóriának egy része a fizikai memória egy rögzített címtartományában elérhet . Magyarra fordítva ez o azt jelenti, hogy grafikus módok esetén a 000A 0000h, míg szöveges módoknál a 000B 0000h (fekete-fehér) vagy a 000B 8000h (színes) fizikai címeken kezd d terület a videokártyán lev o o o memória aktuális állapotát tükrözi, és oda beírva valamit az igazi videomemória is módosulni fog. Grafikus módoknál a 0A000h szegmens teljes terjedelmében felhasználható, míg szöveges mód esetén a 0B000h és 0B800h szegmenseknek csak az els fele (tehát a 0000h ­ 7FFFh o offszetek közötti terület). Bennünket most csak a szöveges módok érdekelnek, azokon belül is a színes képerny k esetén használatosak. Fekete-fehér (monokróm) képet el állító videokártya o o esetében a 0B000h szegmenset kell használni, egyéb változtatásra nincs szükség. A videomemóriában a képerny n látható karakterek sorfolytonosan helyezkednek el, és o minden karaktert egy újabb bájt követ, ami a színinformációt (attribútumot) tartalmazza. Ez pontosan azt jelenti, hogy a karakterek a páros, míg az attribútum-bájtok a páratlan offszetcímeken találhatók. Az attribútumot a következ módon kódolják: a 0 ­ 3 bitek adják az el térszínt, o o a 4 ­ 6 bitek a hátteret, míg a 7-es bit villogást ír el , de úgy is beállítható a videokártya, hogy a o háttérszín 4. bitjét jelentse. A színkódok megegyeznek a már korábban (14.4. táblázat) leírtakkal. Az UjKezelo rutinhoz visszatérve, a bal fels sarokban lev karakter a 0B800h:0000h címen o o helyezkedik el, amit az attribútum követ. Miután mindkét bájtot külön-külön inkrementáltuk,

15.3. REZIDENS PROGRAM KÉSZÍTÉSE

113

IRET-tel visszatérünk a hívóhoz.

A f program szokatlanul rövidre sikerült, hiszen nincs sok feladata. Mindössze a régi o megszakítás-vektort menti el, majd beállítja az újat. Az egyetlen újdonságot az LDS (Load full pointer into DS and a general purpose register) utasítás képviseli, ami a DS:céloperandus regiszterpárba a forrásoperandus által mutatott memóriaterületen lev távoli mutatót (azaz szego menst és offszetet) tölti be. Ez a mostani példában azt jelenti, hogy SI-be az ES:(1Ch · 4), míg DS-be az ES:(1Ch · 4 + 2) címeken lev szavakat tölti be. Hasonló m veletet végez az LES utao u sítás, ami DS helyett ES-t használja. A céloperandus csak egy 16 bites általános célú regiszter, a forrás pedig csak memóriahivatkozás lehet mindkét utasításnál. Ezek után a f program be is fejezi m ködését, de nem a hagyományos módon. Az INT 27h o u egy olyan DOS-megszakítás, ami a program m ködését úgy fejezi be, hogy annak egy része a u memóriában marad (azaz TSR-ré válik). A rezidens rész a CS:DX címig tart, kezdete pedig a PSP szegmense. A PSP-re és a .COM programok szerkezetére a 13. fejezet világít rá. Mi most csak a RegiCim változót és az UjKezelo eljárást hagyjuk a memóriában (meg a PSP-t, de ez most nem lényeges). Az INT 27h-val legfeljebb 64 Kbájtnyi terület tehet rezidenssé, és kilépéskor nem adhatunk o vissza hibakódot (exit code), amit az INT 21h 4Ch funkciójánál AL-ben közölhettünk. Ezeket a kényelmetlenségeket küszöböli ki az INT 21h 31h számú szolgáltatása. Ennek AL-ben megadhatjuk a szokásos visszatérési hibakódot, DX-ben pedig a rezidenssé teend terület méretét kell o megadnunk paragrafusokban (azaz 16 bájtos egységekben), a PSP szegmensét l számítva. o Megszakítás-vektorok beállítására és olvasására a DOS is kínál lehet séget. Az INT 21h o 25h számú szolgáltatása az AL-ben megadott számú megszakítás vektorát a DS:DX által leírt címre állítja be, míg a 35h szolgáltatás az AL számú megszakítás-vektor értékét ES:BX-ben adja vissza.

16. fejezet

Kivételek
A kivétel (exception) egy olyan megszakítás, amit a processzor vált ki, ha egy olyan hibát észlel, ami lehetetlenné teszi a következ utasítás végrehajtását, vagy egy olyan esemény o történt, amir l a programoknak és a felhasználónak is értesülniük kell. Minden kivételt egy o decimális sorszámmal és egy névvel azonosítanak, ezenkívül minden kivételhez tartozik egy kett skeresztb l (#) és két bet b l álló rövidítés, amit szintén mnemoniknak hívnak (ez viszont o o u o nem egy utasításra utal). A 8086-os mikroprocesszor összesen 4 féle kivételt képes generálni. A kivétel kiváltásához szükséges feltételek teljesülése esetén a processzor a verembe berakja a Flags, CS és IP regiszterek tartalmát (szimbolikusan PUSHF // PUSH CS // PUSH IP), majd törli az IF és TF flageket. Ezután sor kerül a kivételt lekezel programrész (exception handler) végrehajtására. Az o Intel az INT 00h ­ INT 1Fh szoftver-megszakításokat a kivétel-kezel programok számára tartja o fenn. Egy adott N sorszámú kivétel esetén az INT N megszakítás kezel je lesz végrehajtva. A o teend k elvégzése után a kezel IRET utasítással visszatérhet abba a programba, amely a kivéo o telt okozta. Bár a kés bbi processzorokon megjelentek olyan, nagyon súlyos rendszerhibát jelz o o kivételek, amik után a kivételt okozó program már nem indítható újra (vagy nem folytatható), a 8086-os processzoron mindegyik kivétel megfelel lekezelése után a hibázó program futása o folytatható. A kivételek 3 típusba sorolhatók: vannak hibák (fault) és csapdák (trap). (A 80286-os processzoron egy harmadik kategória is megjelent, ezek az ú.n. abort-ok.) A két fajta kivétel között az a különbség, hogy míg a fault-ok bekövetkeztekor a verembe mentett CS:IP érték a hibázó utasításra mutat, addig a trap-ek esetén a hibát kiváltó utasítást követ utasítás címét o tartalmazza CS:IP veremben lev másolata. (Az abort kivételek esetén a CS:IP-másolat értéke o általában meghatározatlan.) Ezenkívül a trap-ek (mint nevük is mutatja) a program hibamentesítését, debuggolását támogatják, míg a fault és abort típusú kivételek a kritikus hibákat jelzik, és azonnali cselekvésre szólítanak fel. Most pedig lássuk, milyen kivételek is vannak, és ezek mely feltételek hatására jönnek létre (16.1. táblázat)! A 0-ás kivétel az osztáskor megtörtén hibák során keletkezik. A DIV és az IDIV utasítások o válthatják ki, és alapvet en két oka van: az egyik, hogy nullával próbáltunk meg osztani, ez az o eset általában a hibás algoritmusokban fordul el . A másik ok viszont gyakoribb: akkor is ezt a o kivételt kapjuk, ha az osztás hányadosa nem fér el a célban. Például ha AX = 0100h, BL = 01h, és kiadjuk a DIV BL / IDIV BL utasítások valamelyikét, akkor a hányados is 0100h lenne, ez viszont nem fér el AL-ben (hiszen a hányadost ott kapnánk meg, a maradékot pedig AH-ban).

115 16.1. táblázat. A 8086-os processzoron létez kivételek o

Szám 0 1 3 4

Mnemo #DE #DB #BP #OF

Név Divide Error Debug Breakpoint Overflow

Típus Trap Trap Trap Trap

Kiváltó feltételek
DIV és IDIV utasítások INT 01h utasítás/TF = 1 INT 3 utasítás INTO utasítás, ha OF = 1

Ennek eredménye a 0-ás kivétel lesz. Ezt a kivételt alapesetben a DOS kezeli le, egy hibaüzenet kiírása után egyszer en terminálja az éppen futó programot, majd visszaadja a vezérlést a szül u o alkalmazásnak (COMMAND.COM, valamilyen shell program, commander stb.). Megjegyezzük, hogy a 0-ás kivételt a 80286-os processzortól kezd d en hibaként (fault) o o kezelik. Az 1-es kivétel keletkezésének két oka lehet: vagy kiadtunk egy INT 01h utasítást (kódja 0CDh 01h), vagy bekapcsoltuk a TF flag-et. 3-ast kivételt az INT 3 utasítás tud okozni. Az INT utasításnak erre a megszakítás-számra két alakja is van: az egyik a megszokott kétbájtos forma (0CDh 03h), a másik pedig csak egy bájtot foglal el (0CCh). A két kódolás nem teljesen azonos m ködést vált ki, ez azonban csak a u 80386-os vagy annál újabb processzorokon létez virtuális-8086 üzemmódban nyilvánul meg. o 4-es kivétel akkor keletkezik, ha OF = 1, és kiadunk egy INTO utasítást. Bár nem kivétel, említést érdemel egy korábban még be nem mutatott megszakítás. Az NMI (NonMaskable Interrupt) olyan hardver-megszakítás, ami a megszakítás-vezérl t kikerülve közo vetlenül a processzor egyik lábán (érintkez jén) keresztül jut el a központi egységhez, és amint o neve is mutatja, nem tiltható le. A CPU az IF flag állásától függetlenül mindig ki fogja szolgálni ezt a megszakítást, mégpedig az INT 02h kezel t meghívva. Mivel NMI csak valamilyen o hardverhiba folytán keletkezik (pl. memória-paritáshiba, nem megfelel tápfeszültség stb.), leo kezelése után a számítógép nincs biztonságos állapotban a folytatáshoz, ezért az NMI-kezel ben o általában hibaüzenet kiírása (esetleg hangjelzés adása) után leállítják a m ködést. Ez utóbbit pl. u úgy érik el, hogy kiadnak egy CLI utasítást, majd egy végtelen ciklusba engedik a processzort (ez a legegyszer bben egy önmagára mutató JMP utasítást jelent). Másik megoldás a CLI után u a HLT (HaLT) operandus nélküli utasítás kiadása lehet, aminek hatására ,,álomba szenderül" a processzor, és onnan csak egy hardver-megszakítás, az NMI vagy a reset ,,ébreszti fel." A dologban az a vicces, hogy az NMI keletkezését le lehet tiltani az alaplap egy bizonyos portjára írva. (XT-k és PC esetén a 00A0h porton a legfels bit 0-ás értéke, míg AT-knál a o 0070h port ugyanazon bitjének 1-es értéke tiltja le az NMI-t.) Ez elég kockázatos dolog, hiszen ha egyszer NMI-t észlel az alaplap, és nem képes azt a CPU tudtára hozni, akkor a felhasználó mit sem sejtve folytatja munkáját, közben pedig lehet, hogy több adatot veszít el így, mintha az NMI-t látva resetelné vagy kikapcsolná a gépet. Megjegyezzük, hogy a matematikai koprocesszor (FPU) is generálhat NMI-t. Ebben az esetben az NMI keletkezése nem hardverhibát, hanem a számítások közben el forduló numeo rikus hibákat (kivételeket) jelzi. Az NMI-kezel nek a dolga, hogy eldöntse, le kell-e állítani a o számítógépet avagy nem.

A. Függelék

Átváltás különféle számrendszerek között
A 0 ­ 255 értékek bináris, oktális, decimális és hexadecimális számrendszerek közötti átváltását könnyíti meg az A.1. táblázat. A.1. táblázat. Átváltás a 2-es, 8-as, 10-es és 16-os számrendszerek között

Dec 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 Dec

Bin 0000 0000b 0000 0010b 0000 0100b 0000 0110b 0000 1000b 0000 1010b 0000 1100b 0000 1110b 0001 0000b 0001 0010b 0001 0100b 0001 0110b 0001 1000b 0001 1010b 0001 1100b 0001 1110b 0010 0000b 0010 0010b 0010 0100b 0010 0110b 0010 1000b 0010 1010b 0010 1100b Bin

Oct 000o 002o 004o 006o 010o 012o 014o 016o 020o 022o 024o 026o 030o 032o 034o 036o 040o 042o 044o 046o 050o 052o 054o Oct

Hex 00h 02h 04h 06h 08h 0Ah 0Ch 0Eh 10h 12h 14h 16h 18h 1Ah 1Ch 1Eh 20h 22h 24h 26h 28h 2Ah 2Ch Hex

Dec 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 Dec

Bin 0000 0001b 0000 0011b 0000 0101b 0000 0111b 0000 1001b 0000 1011b 0000 1101b 0000 1111b 0001 0001b 0001 0011b 0001 0101b 0001 0111b 0001 1001b 0001 1011b 0001 1101b 0001 1111b 0010 0001b 0010 0011b 0010 0101b 0010 0111b 0010 1001b 0010 1011b 0010 1101b Bin

Oct 001o 003o 005o 007o 011o 013o 015o 017o 021o 023o 025o 027o 031o 033o 035o 037o 041o 043o 045o 047o 051o 053o 055o Oct

Hex 01h 03h 05h 07h 09h 0Bh 0Dh 0Fh 11h 13h 15h 17h 19h 1Bh 1Dh 1Fh 21h 23h 25h 27h 29h 2Bh 2Dh Hex

117 A.1. táblázat. (folytatás)

Dec 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102 104 106 108 110 112 114 116 118 120 122 124 126 128 130 132 Dec

Bin 0010 1110b 0011 0000b 0011 0010b 0011 0100b 0011 0110b 0011 1000b 0011 1010b 0011 1100b 0011 1110b 0100 0000b 0100 0010b 0100 0100b 0100 0110b 0100 1000b 0100 1010b 0100 1100b 0100 1110b 0101 0000b 0101 0010b 0101 0100b 0101 0110b 0101 1000b 0101 1010b 0101 1100b 0101 1110b 0110 0000b 0110 0010b 0110 0100b 0110 0110b 0110 1000b 0110 1010b 0110 1100b 0110 1110b 0111 0000b 0111 0010b 0111 0100b 0111 0110b 0111 1000b 0111 1010b 0111 1100b 0111 1110b 1000 0000b 1000 0010b 1000 0100b Bin

Oct 056o 060o 062o 064o 066o 070o 072o 074o 076o 100o 102o 104o 106o 110o 112o 114o 116o 120o 122o 124o 126o 130o 132o 134o 136o 140o 142o 144o 146o 150o 152o 154o 156o 160o 162o 164o 166o 170o 172o 174o 176o 200o 202o 204o Oct

Hex 2Eh 30h 32h 34h 36h 38h 3Ah 3Ch 3Eh 40h 42h 44h 46h 48h 4Ah 4Ch 4Eh 50h 52h 54h 56h 58h 5Ah 5Ch 5Eh 60h 62h 64h 66h 68h 6Ah 6Ch 6Eh 70h 72h 74h 76h 78h 7Ah 7Ch 7Eh 80h 82h 84h Hex

Dec 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99 101 103 105 107 109 111 113 115 117 119 121 123 125 127 129 131 133 Dec

Bin 0010 1111b 0011 0001b 0011 0011b 0011 0101b 0011 0111b 0011 1001b 0011 1011b 0011 1101b 0011 1111b 0100 0001b 0100 0011b 0100 0101b 0100 0111b 0100 1001b 0100 1011b 0100 1101b 0100 1111b 0101 0001b 0101 0011b 0101 0101b 0101 0111b 0101 1001b 0101 1011b 0101 1101b 0101 1111b 0110 0001b 0110 0011b 0110 0101b 0110 0111b 0110 1001b 0110 1011b 0110 1101b 0110 1111b 0111 0001b 0111 0011b 0111 0101b 0111 0111b 0111 1001b 0111 1011b 0111 1101b 0111 1111b 1000 0001b 1000 0011b 1000 0101b Bin

Oct 057o 061o 063o 065o 067o 071o 073o 075o 077o 101o 103o 105o 107o 111o 113o 115o 117o 121o 123o 125o 127o 131o 133o 135o 137o 141o 143o 145o 147o 151o 153o 155o 157o 161o 163o 165o 167o 171o 173o 175o 177o 201o 203o 205o Oct

Hex 2Fh 31h 33h 35h 37h 39h 3Bh 3Dh 3Fh 41h 43h 45h 47h 49h 4Bh 4Dh 4Fh 51h 53h 55h 57h 59h 5Bh 5Dh 5Fh 61h 63h 65h 67h 69h 6Bh 6Dh 6Fh 71h 73h 75h 77h 79h 7Bh 7Dh 7Fh 81h 83h 85h Hex

118 A.1. táblázat. (folytatás)

Dec 134 136 138 140 142 144 146 148 150 152 154 156 158 160 162 164 166 168 170 172 174 176 178 180 182 184 186 188 190 192 194 196 198 200 202 204 206 208 210 212 214 216 218 220 Dec

Bin 1000 0110b 1000 1000b 1000 1010b 1000 1100b 1000 1110b 1001 0000b 1001 0010b 1001 0100b 1001 0110b 1001 1000b 1001 1010b 1001 1100b 1001 1110b 1010 0000b 1010 0010b 1010 0100b 1010 0110b 1010 1000b 1010 1010b 1010 1100b 1010 1110b 1011 0000b 1011 0010b 1011 0100b 1011 0110b 1011 1000b 1011 1010b 1011 1100b 1011 1110b 1100 0000b 1100 0010b 1100 0100b 1100 0110b 1100 1000b 1100 1010b 1100 1100b 1100 1110b 1101 0000b 1101 0010b 1101 0100b 1101 0110b 1101 1000b 1101 1010b 1101 1100b Bin

Oct 206o 210o 212o 214o 216o 220o 222o 224o 226o 230o 232o 234o 236o 240o 242o 244o 246o 250o 252o 254o 256o 260o 262o 264o 266o 270o 272o 274o 276o 300o 302o 304o 306o 310o 312o 314o 316o 320o 322o 324o 326o 330o 332o 334o Oct

Hex 86h 88h 8Ah 8Ch 8Eh 90h 92h 94h 96h 98h 9Ah 9Ch 9Eh 0A0h 0A2h 0A4h 0A6h 0A8h 0AAh 0ACh 0AEh 0B0h 0B2h 0B4h 0B6h 0B8h 0BAh 0BCh 0BEh 0C0h 0C2h 0C4h 0C6h 0C8h 0CAh 0CCh 0CEh 0D0h 0D2h 0D4h 0D6h 0D8h 0DAh 0DCh Hex

Dec 135 137 139 141 143 145 147 149 151 153 155 157 159 161 163 165 167 169 171 173 175 177 179 181 183 185 187 189 191 193 195 197 199 201 203 205 207 209 211 213 215 217 219 221 Dec

Bin 1000 0111b 1000 1001b 1000 1011b 1000 1101b 1000 1111b 1001 0001b 1001 0011b 1001 0101b 1001 0111b 1001 1001b 1001 1011b 1001 1101b 1001 1111b 1010 0001b 1010 0011b 1010 0101b 1010 0111b 1010 1001b 1010 1011b 1010 1101b 1010 1111b 1011 0001b 1011 0011b 1011 0101b 1011 0111b 1011 1001b 1011 1011b 1011 1101b 1011 1111b 1100 0001b 1100 0011b 1100 0101b 1100 0111b 1100 1001b 1100 1011b 1100 1101b 1100 1111b 1101 0001b 1101 0011b 1101 0101b 1101 0111b 1101 1001b 1101 1011b 1101 1101b Bin

Oct 207o 211o 213o 215o 217o 221o 223o 225o 227o 231o 233o 235o 237o 241o 243o 245o 247o 251o 253o 255o 257o 261o 263o 265o 267o 271o 273o 275o 277o 301o 303o 305o 307o 311o 313o 315o 317o 321o 323o 325o 327o 331o 333o 335o Oct

Hex 87h 89h 8Bh 8Dh 8Fh 91h 93h 95h 97h 99h 9Bh 9Dh 9Fh 0A1h 0A3h 0A5h 0A7h 0A9h 0ABh 0ADh 0AFh 0B1h 0B3h 0B5h 0B7h 0B9h 0BBh 0BDh 0BFh 0C1h 0C3h 0C5h 0C7h 0C9h 0CBh 0CDh 0CFh 0D1h 0D3h 0D5h 0D7h 0D9h 0DBh 0DDh Hex

119 A.1. táblázat. (folytatás)

Dec 222 224 226 228 230 232 234 236 238 240 242 244 246 248 250 252 254 Dec

Bin 1101 1110b 1110 0000b 1110 0010b 1110 0100b 1110 0110b 1110 1000b 1110 1010b 1110 1100b 1110 1110b 1111 0000b 1111 0010b 1111 0100b 1111 0110b 1111 1000b 1111 1010b 1111 1100b 1111 1110b Bin

Oct 336o 340o 342o 344o 346o 350o 352o 354o 356o 360o 362o 364o 366o 370o 372o 374o 376o Oct

Hex 0DEh 0E0h 0E2h 0E4h 0E6h 0E8h 0EAh 0ECh 0EEh 0F0h 0F2h 0F4h 0F6h 0F8h 0FAh 0FCh 0FEh Hex

Dec 223 225 227 229 231 233 235 237 239 241 243 245 247 249 251 253 255 Dec

Bin 1101 1111b 1110 0001b 1110 0011b 1110 0101b 1110 0111b 1110 1001b 1110 1011b 1110 1101b 1110 1111b 1111 0001b 1111 0011b 1111 0101b 1111 0111b 1111 1001b 1111 1011b 1111 1101b 1111 1111b Bin

Oct 337o 341o 343o 345o 347o 351o 353o 355o 357o 361o 363o 365o 367o 371o 373o 375o 377o Oct

Hex 0DFh 0E1h 0E3h 0E5h 0E7h 0E9h 0EBh 0EDh 0EFh 0F1h 0F3h 0F5h 0F7h 0F9h 0FBh 0FDh 0FFh Hex

B. Függelék

Karakter kódtáblázatok
A karakterek kódolására két szabvány terjedt el. Ezek az ASCII (American Standard Code for Information Interchange) és az EBCDIC (Extended Binary-Coded Decimal Interchange Code) nevet kapták. Az ASCII eredetileg 7 bites volt (tehát csak 128 karaktert tartalmazott), kés bb b vítették csak ki 8 bitesre. A fels 128 pozícióban általában különféle nyelvek bet it o o o u és speciális jeleket tárolnak. Mi itt az ú.n. Latin-2 (más néven ISO 8859-2, 1250-es kódlap) kiosztást mutatjuk be, ez tartalmazza ugyanis az összes közép európai nyelv (köztük a magyar) bet it is. u Mindkét szabvány esetén néhány karakternek különleges funkciója van. Ezeket számos periféria (pl. modem, nyomtató, terminál) vezérlési célokra használja. Az ilyen karaktereknek általában nincs nyomtatható (megjeleníthet ) képe. Ehelyett egy néhány bet s név utal szereo u pükre. Az ismert rövidítések jelentését mutatja a B.1. táblázat. B.1. táblázat. ASCII és EBCDIC vezérl kódok o

Név ACK BEL BS CAN CR DC1 DC2 DC3 DC4 DEL DLE EM ENQ EOT ESC ETB ETX FS FF

Jelentés ACKnowledge BELl BackSpace CANcel Carriage Return Device Control 1 (X-ON) Device Control 2 Device Control 3 (X-OFF) Device Control 4 DELete Data Line Escape End of Medium ENQuiry End Of Transmission ESCape End of Transmission Block End of TeXt File Separator Form Feed

121 B.1. táblázat. (folytatás)

Név GS HT LF NAK NUL RS SI SO SOH SP STX SUB SYN US VT

Jelentés Group Separator Horizontal Tabulator Line Feed Negative AcKnowledge NULl (end of string) Record Separator Shift In Shift Out Start Of Heading SPace Start of TeXt SUBstitute SYNchronous idle Unit Separator Vertical Tabulator

A B.2. táblázat tartalmazza az említett két szabvány beosztását. Az üresen hagyott helyek funkciója ismeretlen. B.2. táblázat. ASCII és EBCDIC karakterkódok

Kód 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 Kód
1 Látható

ASCII NUL STX EOT ACK BS LF FF SO DLE DC2 DC4 SYN CAN SUB FS RS SP 1 " $ & ASCII

EBCDIC NUL STX PF LC
SMM FF SO DLE DC2 RES BS CAN CC IFS IRS DS FS BYP ETB EBCDIC

Kód 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 Kód

ASCII SOH ETX ENQ BEL HT VT CR SI DC1 DC3 NAK ETB EM ESC GS US ! # % ' ASCII

EBCDIC SOH ETX HT DEL
VT CR SI DC1 TM NL IL EM CU1 IGS IUS SOS LF ESC EBCDIC

szóköz.

122 B.2. táblázat. (folytatás)

Kód 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102 104 106 108 110 112 114 116 118 120 122 124 126 Kód

ASCII ( * , . 0 2 4 6 8 : < > @ B D F H J L N P R T V X Z \ ^ ` b d f h j l n p r t v x z ¦ ~ ASCII

EBCDIC
SM ACK SYN PN UC

DC4 SP

¢ < + &

! * ;

% >

: @ = EBCDIC

Kód 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99 101 103 105 107 109 111 113 115 117 119 121 123 125 127 Kód

ASCII ) + - / 1 3 5 7 9 ; = ? A C E G I K M O Q S U W Y [ ] _ a c e g i k m o q s u w y { } DEL ASCII

EBCDIC
CU2 ENQ BEL

RS EOT CU3 NAK SUB

. { ¦

$ ) /

, _ ?

# ' " EBCDIC

123 B.2. táblázat. (folytatás)

Kód 128 130 132 134 136 138 140 142 144 146 148 150 152 154 156 158 160 162 164 166 168 170 172 174 176 178 180 182 Kód
2 Pontnélküli 3 ,,Grave"

ASCII

EBCDIC
b d f h

i2 ~5 6 °8
10 12

k m o q

13 ¤ ´ S ¨ 14 S ¸ ´ Z Z ° 16
17

s u w y

´ 18 ´ s ASCII

EBCDIC

Kód 129 131 133 135 137 139 141 143 145 147 149 151 153 155 157 159 161 163 165 167 169 171 173 175 177 179 181 183 Kód

ASCII

EBCDIC a c e g i

`3 ^4 7

j l n p r

9 11 A L L ' § S T
15

t v x z

Z a l l' 19 ASCII

EBCDIC

,,i". ékezet. 4 ,,Circumflex" vagy ,,hat" ékezet. 5 ,,Tilde" ékezet. 6 ,,Breve" ékezet. 7 ,,Dot" ékezet. 8 ,,Ring" ékezet. 9 ,,Double acute" vagy ,,hungarian umlaut" ékezet. 10 ,,Ogonek" ékezet. 11 ,,Ha ek" vagy ,,caron" ékezet. c 12 Nemtörhet szóköz. o 13 ,,Breve" ékezet. 14 ,,Dieresis" vagy ,,umlaut" ékezet. 15 Feltételes köt jel (soft hyphen). o 16 Fokjel. 17 ,,Ogonek" ékezet. 18 ,,Acute" ékezet. 19 ,,Ha ek" vagy ,,caron" ékezet. c

124 B.2. táblázat. (folytatás)

Kód 184 186 188 190 192 194 196 198 200 202 204 206 208 210 212 214 216 218 220 222 224 226 228 230 232 234 236 238 240 242 244 246 248 250 252 254 Kód

ASCII ¸ 20 s ¸ z ´ z ´ R Â Ä ´ C C E E Î Ð N Ô Ö R Ú Ü T ¸ ´ r â ä ´ c c e e î d ¯ n ô ö r ú ü ¸ t ASCII

EBCDIC

B D F H

K M O Q

S U W Y

0 2 4 6 8

EBCDIC

Kód 185 187 189 191 193 195 197 199 201 203 205 207 209 211 213 215 217 219 221 223 225 227 229 231 233 235 237 239 241 243 245 247 249 251 253 255 Kód

ASCII s t' 21 z Á A ´ L Ç É Ë Í D ´ N Ó O × ° U U Ý ß á a ´ l ç é ë í d' ´ n ó o ÷ u ° u ý 22 ASCII

EBCDIC

A C E G I

J L N P R

T V X Z

1 3 5 7 9

EBCDIC

20 ,,Cedilla" 21

ékezet. ,,Double acute" vagy ,,hungarian umlaut" ékezet. 22 ,,Dot" ékezet.

Tárgymutató
A, Á abort-class exception . . . . . . . . . . . . . . . . 114 acknowledgement . . . . . . . . . lásd nyugtázás adatmozgató utasítások . . . . . . . . . . . . . . . 30 address space . . . . . . . . . . . . . lásd címterület wrap-around . . . . . . . . . . . . . . . . . . . lásd címterület-körbefordulás addressing mode . . . . . . . lásd címzési mód alaplap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 áloperandus . . . . . . . . . . . . . . . . . . . . . . . . . 75 általános célú adatregiszter . . . . . . . . . . . . 14 általános célú regiszterek . . . . . . . . . . . . . .17 antivalencia . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 aritmetikai flag . . . . . . . . . . . . . . . . . . . . . . . 17 aritmetikai shiftelés . . . . . . . . . . . . . . . . . . 62 ASCII kódtábla . . . . . . . . . . . . . . . . . . . . . 120 ASCIIZ sztring . . . . . . . . . . . . . . . . . . . . . . 98 assembler . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 átvitel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 borrow . . . . . . . . . . . . . . . . . . . . . . . . . 12 carry . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 B backlink . . . . . . . . . . . . . lásd visszaláncolás bájt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 BCD aritmetikai utasítások . . . . . . . . . . . . 32 BCD szám . . . . . . 17, lásd binárisan kódolt decimális egész számok pakolatlan . . . . . . . . . . . . . . . . . . . . . . 17 pakolt . . . . . . . . . . . . . . . . . . . . . . . . . . 17 belépési pont . . . . . . . . . . . . . . . . . . . . . . . . 89 big-endian tárolásmód . . . . . . . . . . . . . . . . . 8 bináris m velet . . . . . . . . . . . . . . . . . . . . . . . . . . 7 u számjegy . . . . . . . . . . . . . . . . . . . lásd bit számrendszer . . . . . . . . . . . . . . . . . . . . . 7 binárisan kódolt decimális egész számok17 BIOS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 beállítása . . . . . . . . . . . . . . . . . . . . . . . 65 invertálása . . . . . . . . . . . . . . . . . . . . . . 65 tesztelése . . . . . . . . . . . . . . . . . . . . . . . 65 törlése . . . . . . . . . . . . . . . . . . . . . . . . . . 65 bitléptet utasítások . . . . . . . . . . . . . . . . . . 32 o bitmaszk . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 borrow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 busz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 buszlezáró prefix . . . . . . . . . . . . . . . . . . . . . 30 byte . . . . . . . . . . . . . . . . . . . . . . . . . . . lásd bájt C carry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 céloperandus . . . . . . . . . . . . . . . . . . . . . . . . 26 címke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 címterület . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 -körbefordulás . . . . . . . . . . . . . . . . . . .14 címzés bázis . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 bázis+index . . . . . . . . . . . . . . . . . . . . . 18 bázis+index+relatív . . . . . . . . . . . . . .18 bázis+relatív, bázisrelatív . . . . . . . . .18 index . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 index+relatív, indexrelatív . . . . . . . . 18 közvetlen . . . . . . . . . . . . . . . . . . . . . . . 18 címzési mód . . . . . . . . . . . . . . . . . . . . . . . . . 18 CPU . . . . . . . . . . . . 5, lásd mikroprocesszor D decimális számrendszer . . . . . . . . . . . . . . . . 7 direkt ugrás . . . . . . . . . . . . . . . . . . . . . . . . .105 direktíva . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 .STACK . . . . . . . . . . . . . . . . . . . . . . . . 38 ASSUME . . . . . . . . . . . . . . . . . . . . . . . 38 DB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 DD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 DF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 DOSSEG . . . . . . . . . . . . . . . . . . . . . . . 88 DQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

TÁRGYMUTATÓ

126 fejléc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 feltételes ugrás . . . . . . . . . . . . . . . . . . . . . . . 45 file handle . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 fizikai memóriacím . . . . . . . . . . . . . . . . . . . 14 flag . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 aritmetikai . . . . . . . . . . . . . . . . . . . . . . 17 Flags regiszter . . . . . . . . . . . . . . . . . . . . . . . 14 forgatás . . . . . . . . . . . . . . . . . . . . . lásd rotálás forrásoperandus . . . . . . . . . . . . . . . . . . . . . . 26 frissítés. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6 function . . . . . . . . . . . . . . . . . . lásd függvény függvény . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 G gépi kód . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 giga- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 H hardver-megszakítás . . . . . . . . . . . . . . . . . . 23 header . . . . . . . . . . . . . . . . . . . . . . . lásd fejléc hexadecimális számrendszer . . . . . . . . . . . . 7 I, Í id zít . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 o o indexregiszter . . . . . . . . . . . . . . . . . . . . . . . . 14 indirekt ugrás . . . . . . . . . . . . . . . . . . . . . . . 105 init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 instruction . . . . . . . . . . . . . . . . . . lásd utasítás interrupt . . . . . . . . . . . . . . . lásd megszakítás K karakteres konstans . . . . . . . . . . . . . . . . . . . 25 kettes komplemens alak . . . . . . . . . . . . . . . . 9 kilo- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 kivétel . . . . . . . . . . . . . . . . . . . . . . . . . . 23, 114 konjunkció . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 koprocesszor-vezérl utasítások . . . . . . . 33 o közeli ugrás . . . . . . . . . . . . . . . . . . . . . . . . 105 közvetlen memória-hozzáférés . lásd DMA kvadraszó . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 L lefelé b vül verem . . . . . . . . . . . . . . . . . . 20 o o léptetés . . . . . . . . . . . . . . . . . . . . lásd shiftelés LIFO elv . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 lineáris memóriacím . . . . . . . . . . . . . . . . . . 13 lineáris memória-modell . . . . . . . . . . . . . . 13 linker . . . . . . . . . . . . . . . . . . . . lásd szerkeszt o little-endian tárolásmód . . . . . . . . . . . . . . . . 8

DT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 DW . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 END . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 ENDP . . . . . . . . . . . . . . . . . . . . . . . . . . 50 ENDS . . . . . . . . . . . . . . . . . . . . . . . . . . 38 EQU . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 JUMPS . . . . . . . . . . . . . . . . . . . . . . . . . 46 LABEL . . . . . . . . . . . . . . . . . . . . . . . . . 88 MODEL . . . . . . . . . . . . . . . . . . . . . . . . 38 NOJUMPS . . . . . . . . . . . . . . . . . . . . . 46 ORG . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 PROC . . . . . . . . . . . . . . . . . . . . . . . . . . 50 SEGMENT . . . . . . . . . . . . . . . . . . . . . 38

disassemblálás . . . . . . . . . . . . . . . . . . . . . . . 52 displacement . . . . . . . . . . . . . . . . lásd eltolás diszjunkció . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 -vezérl . . . . . . . . . . . . . . . . . . . . . . . . . . 6 o dollárjel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 doubleword . . . . . . . . . . . . . . . lásd duplaszó duplaszó . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8 E, É EBCDIC kódtábla . . . . . . . . . . . . . . . . . . . 120 effektív cím . . . . . . . . . . . lásd tényleges cím egész aritmetikai utasítások . . . . . . . . . . . 31 egyes komplemens alak . . . . . . . . . . . . . . . . 9 egyszer sített szegmensdefiníció . . . . . . . 27 u eljárás. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .67 eljáráshívás . . . . . . . . . . . . . . . . . . . . . . . . . . 49 el jel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 o -bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 el jeles kiterjesztés . . . . . . . . . . . . . . . . . . . 12 o el jeles számok . . . . . . . . . . . . . . . . . . . . . . . 9 o el jeltelen kiterjesztés . . . . . . . . . . . . . . . . 12 o el jeltelen számok . . . . . . . . . . . . . . . . . . . . . 8 o eltolás. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .18 endianizmus . . . . . . . . . . . . . . . . . . . . . . . . . . 8 big-endian tárolásmód . . . . . . . . . . . . . 8 little-endian tárolásmód . . . . . . . . . . . 8 entry point . . . . . . . . . . . . lásd belépési pont exception . . . . . . . . . . . . . . . . . . . lásd kivétel abort . . . . . . . . . . . . . . . . . . . . . . . . . . 114 fault . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 trap . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 F fault-class exception . . . . . . . . . . . . . . . . . 114

TÁRGYMUTATÓ

127 negáció . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 nibble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 NMI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 normált memóriacím . . . . . . . . . . . . . . . . . 14 numerikus konstans . . . . . . . . . . . . . . . . . . 25 Ny nyugtázás . . . . . . . . . . . . . . . . . . . . . . . . . . 110 O, Ó object code . . . . . . . . . . . . . . . . lásd tárgykód offszetcím . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 oktális számrendszer . . . . . . . . . . . . . . . . . . . 7 opcode . . . . . . . . . . . . . . . . lásd m veleti kód u operandus . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 operátor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 DUP . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 OFFSET . . . . . . . . . . . . . . . . . . . . . . . 79 PTR . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 SHORT . . . . . . . . . . . . . . . . . . . . . . . 110 órajel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 -generátor . . . . . . . . . . . . . . . . . . . . . . . . 6 P paragrafus . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 paragrafus-határ . . . . . . . . . . . . . . . . . . . . . .13 periféria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 pointer . . . . . . . . . . . . . . . . . . . . . .lásd mutató far, long, full . . . lásd távoli-, hosszú-, teljes mutató near, short . lásd közeli-, rövid mutató port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 pozíció-számláló . . . . . . . . . . . lásd dollárjel prefix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 buszlezáró . . . . . . . . . . . . . . . . . . . . . . 30 CS: . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 DS: . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 ES: . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 LOCK . . . . . . . . . . . . . . . . . . . . . . . . . . 30 REP, REPE, REPZ . . . . . . . . . . 30, 78 REPNE, REPNZ . . . . . . . . . . . . . 30, 78 SEGCS . . . . . . . . . . . . . . . . . . . . . . . . 29 SEGDS . . . . . . . . . . . . . . . . . . . . . . . . 29 SEGES . . . . . . . . . . . . . . . . . . . . . . . . 29 SEGSS . . . . . . . . . . . . . . . . . . . . . . . . 29 SS: . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 szegmensfelülbíráló . . . . . . . . . . 19, 29 sztringutasítást ismétl . . . . . . . . . . . 30 o procedure . . . . . . . . . . . . . . . . . . . lásd eljárás

logikai memóriacím . . . . . . . . . . . . . . . . . . 13 logikai m velet . . . . . . . . . . . . . . . . . . . . . . . 7 u ÉS . . . . . . . . . . . . . . . . . lásd konjunkció KIZÁRÓ VAGY . . . . lásd antivalencia MEGENGEDO VAGY . . . . . . . . . lásd diszjunkció tagadás . . . . . . . . . . . . . . . . lásd negáció logikai shiftelés . . . . . . . . . . . . . . . . . . . . . . 62 logikai utasítások . . . . . . . . . . . . . . . . . . . . 31 M machine code . . . . . . . . . . . . . . . . . . . . . . . . . 2 MASM . . lásd Microsoft Macro Assembler MCB . . . . . . . . . lásd memóriavezérl blokk o mega- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 megjegyzés . . . . . . . . . . . . . . . . . . . . . . . . . . 26 megszakítás . . . . lásd megszakítás-kérelem hardver- . . . . . . . . . . . . . . . . . . . . . . . . 23 -kérelem . . . . . . . . . . . . . . . . . . . . . . . . . 5 -kezel . . . . . . . . . . . . . . . . . . . . . 23, 102 o -maszkolás . . . . . . . . . . . . . . . . . . . . . . 23 -rendszer . . . . . . . . . . . . . . . . . . . . . . . . . 5 szoftver- . . . . . . . . . . . . . . . . . . . . . . . . 23 -vektor . . . . . . . . . . . . . . . . . . . . . . . . 102 -vezérl . . . . . . . . . . . . . . . . . . . . . . . . . . 5 o -vonal . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 memória. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 RAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 ROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 memóriacím fizikai . . . . . . . . . . . . . . . . . . . . . . . . . . 14 lineáris . . . . . . . . . . . . . . . . . . . . . . . . . 13 logikai . . . . . . . . . . . . . . . . . . . . . . . . . . 13 normált . . . . . . . . . . . . . . . . . . . . . . . . . 14 memória-modell . . . . . . . . . . . . . . . . . . 13, 27 lineáris . . . . . . . . . . . . . . . . . . . . . . . . . 13 szegmentált . . . . . . . . . . . . . . . . . . . . . 13 memória-szervezés . . lásd memória-modell memóriavezérl blokk . . . . . . . . . . . . . . . . 81 o Microsoft Macro Assembler . . . . . . . . . . . 28 mikroprocesszor . . . . . . . . . . . . . . . . . . . . . . 4 mnemonik . . . . . . . . . . . . . . . . . . . . . . . . 2, 25 mutató . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 közeli-, rövid- . . . . . . . . . . . . . . . . . . . 17 távoli-, hosszú-, teljes- . . . . . . . . . . . 17 mutatóregiszter . . . . . . . . . . . . . . . . . . . . . . 14 m veleti kód . . . . . . . . . . . . . . . . . . . . . . . . . 25 u N

TÁRGYMUTATÓ

128 el jeltelen . . . . . . . . . . . . . . . . . . . . . . . . 8 o pakolatlan BCD . . . . . . . . . . . . . . . . . 17 pakolt BCD . . . . . . . . . . . . . . . . . . . . . 17 számrendszer bináris . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 decimális . . . . . . . . . . . . . . . . . . . . . . . . 7 hexadecimális . . . . . . . . . . . . . . . . . . . . 7 oktális . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 szegmens . . . . . . . . . . . . . . . . . . . . . . . . 13, 26 szegmens báziscím . . . . . . . . . . . . . . . . . . . 13 szegmenscím . . . . . lásd szegmens báziscím szegmensen belüli ugrás . lásd közeli ugrás szegmensfelülbíráló prefix . . . . . . . . . 19, 29 szegmensközi ugrás . . . . . lásd távoli ugrás szegmensregiszter . . . . . . . . . . . . . . . . . . . . 15 szegmentált memória-modell . . . . . . . . . . 13 szerkeszt . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 o szimbólum . . . . . . . . . . . . . . . . . . . . . . . . . . 25 szó . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 szoftver-megszakítás . . . . . . . . . . . . . . . . . 23 sztring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 sztringkezel utasítások . . . . . . . . . . . . . . . 32 o sztringutasítást ismétl prefixek . . . . . . . .30 o T tárgykód . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 TASM . . . . . . . . . . . . lásd Turbo Assembler távoli ugrás . . . . . . . . . . . . . . . . . . . . . . . . . 105 TD . . . . . . . . . . . . . . . . lásd Turbo Debugger tényleges cím . . . . . . . . . . . . . . . . . . . . . . . . 20 TLINK . . . . . . . . . . . . . . . lásd Turbo Linker töréspont . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 trap-class exception . . . . . . . . . . . . . . . . . 114 TSR . . . . . . . . . . . . . . lásd rezidens program túlcsordulás . . . . . . . . . . . . . . . . . . . . . . . . . 12 Turbo Assembler . . . . . . . . . . . . . . . . . . . . . 28 Turbo Debugger . . . . . . . . . . . . . . . . . . . . . .52 Turbo Linker . . . . . . . . . . . . . . . . . . . . . . . . 28 U, Ú ugrás közeli . . . . . . . . . . . . . . . . . . . . . . . . . 105 rövid . . . . . . . . . . . . . . . . . . . . . . . . . . 110 távoli . . . . . . . . . . . . . . . . . . . . . . . . . . 105 ugrótábla . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 új sor karakterpár . . . . . . . . . . . . . . . . . . . . 51 unáris m velet . . . . . . . . . . . . . . . . . . . . . . . . 7 u utasítás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

programming language . . . . . . . . . . . . . . . . 1 high-level . . . . . . . . . . . . . . . . . . . . . . . . 1 low-level . . . . . . . . . . . . . . . . . . . . . . . . . 1 programozási nyelv . . . . . . . . . . . . . . . . . . . . 1 alacsony szint . . . . . . . . . . . . . . . . . . . 1 u magas szint . . . . . . . . . . . . . . . . . . . . . 1 u programszegmens . . . . . . . . . . . . . . . . . . . . 84 programszegmens-prefix . . . . . . . . . . . . . . 85 PSP . . . . . . . . lásd programszegmens-prefix Q quadword . . . . . . . . . . . . . . . . lásd kvadraszó R RAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 regiszter . . . . . . . . . . . . . . . . . . . . . . . . . . 5, 14 általános célú . . . . . . . . . . . . . . . . 14, 17 Flags . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 index- . . . . . . . . . . . . . . . . . . . . . . . . . . 14 mutató- . . . . . . . . . . . . . . . . . . . . . . . . . 14 státusz- . . . . . . . . . . . . . . . . . . . . . . . . . 14 szegmens- . . . . . . . . . . . . . . . . . . . . . . 15 regiszterpár . . . . . . . . . . . . . . . . . . . . . . . . . . 17 relokáció . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 relokációs tábla . . . . . . . . . . . . . . . . . . . . . . 89 rendszervezérl utasítások . . . . . . . . . . . . 33 o reset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 rezidens program . . . . . . . . . . . . . . . . . . . 111 ROM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5 rotálás . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 rövid ugrás . . . . . . . . . . . . . . . . . . . . . . . . . 110 S scan code . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 shiftelés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 aritmetikai . 62, lásd el jeles shiftelés o el jeles . . . . . . . . . . . . . . . . . . . . . . . . . 12 o el jeltelen . . . . . . . . . . . . . . . . . . . . . . 12 o logikai . . 62, lásd el jeltelen shiftelés o sign . . . . . . . . . . . . . . . . . . . . . . . . . lásd el jel o sign extension . . . . lásd el jeles kiterjesztés o speciális utasítások . . . . . . . . . . . . . . . . . . . 33 stack . . . . . . . . . . . . . . . . . . . . . . . . lásd verem státuszregiszter . . . . . . . . . . . . . . . . . . . . . . 14 Sz szám binárisan kódolt decimális egész . . 17 el jeles . . . . . . . . . . . . . . . . . . . . . . . . . . 9 o

TÁRGYMUTATÓ

129
NOT . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 OR . . . . . . . . . . . . . . . . . . . . . . . . . 44, 65 OUT . . . . . . . . . . . . . . . . . . . . . . . . . . 110 POP . . . . . . . . . . . . . . . . . . . . . . . . 20, 44 POPF . . . . . . . . . . . . . . . . . . . . . . . . . . 66 PUSH . . . . . . . . . . . . . . . . . . . . . . .20, 44 PUSHF . . . . . . . . . . . . . . . . . . . . . . . . . 66 RCL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 RCR . . . . . . . . . . . . . . . . . . . . . . . . . . . 63

AAA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 AAD . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 AAM . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 AAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

adatmozgató . . . . . . . . . . . . . . . . . . . . 30 ADC . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 ADD . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 AND . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 BCD aritmetikai . . . . . . . . . . . . . . . . . 32 bitléptet . . . . . . . . . . . . . . . . . . . . . . . 32 o CALL . . . . . . . . . . . . . . . . . . . . . . 49, 105 CBW . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 CLC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 CLD . . . . . . . . . . . . . . . . . . . . . . . . 44, 66 CLI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 CMC . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 CMP . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 CMPS, CMPSB, CMPSW . . . . . . . 77 CWD . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 DAA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 DAS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 DEC . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 DIV . . . . . . . . . . . . . . . . . . . . . . . . . 50, 58 egész aritmetikai . . . . . . . . . . . . . . . . 31 HLT . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 IDIV . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 IMUL . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 IN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 INC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 INT . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 Jccc (feltételes ugrások) . . . . . . . . . . 45 JMP . . . . . . . . . . . . . . . . . . . . . . . 47, 105 JMPS . . . . . . . . . . . . . . . . . . . . . . . . . 110 koprocesszor-vezérl . . . . . . . . . . . . . 33 o LAHF . . . . . . . . . . . . . . . . . . . . . . . . . . 66 LDS . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 LEA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 LES . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 LODS, LODSB, LODSW . . . . . 44, 79 logikai . . . . . . . . . . . . . . . . . . . . . . . . . . 31 LOOP . . . . . . . . . . . . . . . . . . . . . . . . . . 42 LOOPE, LOOPZ . . . . . . . . . . . . . . . . 43 LOOPNE, LOOPNZ . . . . . . . . . . . . . 43 MOV . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 MOVS, MOVSB, MOVSW . . . . . . . 77 MUL . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 NEG . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 NOP . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

rendszervezérl . . . . . . . . . . . . . . . . . .33 o RET . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 ROL . . . . . . . . . . . . . . . . . . . . . . . . . . . .63 ROR . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 SAHF . . . . . . . . . . . . . . . . . . . . . . . . . . 66 SAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 SAR . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 SBB . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 SCAS, SCASB, SCASW . . . . . . . . .79 SHL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 SHR . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 speciális . . . . . . . . . . . . . . . . . . . . . . . . 33 STC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 STD . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 STI . . . . . . . . . . . . . . . . . . . . . . . . . . . . .66 STOS, STOSB, STOSW . . . . . 47, 79 SUB . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 sztringkezel . . . . . . . . . . . . . . . . . . . . 32 o TEST . . . . . . . . . . . . . . . . . . . . . . . . . . 65 vezérlésátadó . . . . . . . . . . . . . . . . . . . .32 XCHG . . . . . . . . . . . . . . . . . . . . . . . . . . 95 XLAT, XLATB . . . . . . . . . . . . . . . . . . . 95 XOR . . . . . . . . . . . . . . . . . . . . . . . . 39, 65 V verem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 vezérlésátadó utasítások . . . . . . . . . . . . . . 32 visszaláncolás . . . . . . . . . . . . . . . . . . . . . . . 85 W word . . . . . . . . . . . . . . . . . . . . . . . . . . . lásd szó Z zero extension . . . . . . lásd zéró-kiterjesztés zéró-kiterjesztés . . . . . . . . . . lásd el jeltelen o kiterjesztés

Irodalomjegyzék
[1] Dr. Kovács Magda, 32 Bites Mikroprocesszorok 80386/80486 I./II., LSI, Budapest, [könyv] [2] Abonyi Zsolt, PC Hardver Kézikönyv, [könyv] [3] Dan Rollins, Tech Help! v4.0, Copyright © Flambeaux Software, Inc., 1985, 1990, [program] [4] David Jurgens, HelpPC v2.10, Copyright © 1991, [program] [5] Expert Help Hypertext System v1.09, Copyright © SofSolutions, 1990, 1992, [program] [6] Ralf Brown, Ralf Brown's Interrupt List Release 61, Copyright © 1989, 2000, [program, txt] [7] Nobody, The Interrupts and Ports database v1.01, Copyright © 1988, [ng] [8] Morten Elling, Borland's Turbo Assembler v4.0 Ideal mode syntax, Copyright © 1995, [ng] [9] The Assembly Language Database, Copyright © Peter Norton Computing, Inc., 1987, [ng] [10] P. H. Rankin Hansen (Ping), The Programmers Reference v0.02b, [ng] [11] Intel iAPx86 Instruction Set, 1996, [ng] [12] http://www.pobox.com/~ralf/, Ralf Brown's Homepage, [www] Ebben a gy jteményben mind nyomtatott, mind elektronikus formájú m vek, illetve segédu u programok megtalálhatók. A bejegyzések utolsó tagja utal a forrás típusára: · [könyv] ­ Nyomtatásban megjelent könyv · [program] ­ Segédprogram · [ng] ­ Norton Guide formátumú (.ng kiterjesztés ) adatbázis u · [txt] ­ Tisztán szöveges formátumú dokumentum · [www] ­ World Wide Web (Internet) honlap címe

Hasonló témájú dokumentumok
A mások által feltöltött dokumentumokat értékelheted. Ha úgy ítéled meg, hogy a vizsgára való felkészülés szempontjából hasznos volt egy dokumentum, akkor adj rá sokcsillagos értékelést.
Ha hibákat tartalmaz, vagy egyéb probléma van vele, akkor keveset.
A dokumentumok sorrendje az értékelések alapján adódik. Ami fentebb van a listában, azt hasznosabbnak ítélték társaid. Az új dokumentumok pedig (értékelések hiányában) szintén a lista tetején kezdenek.

Hozzászólások

Ha észrevételed van egy dokumentummal kapcsolatban (például hibát találtál benne), akkor a Hozzászólások részben jelezheted. Az olyan jellegű kérdéseket mint pl.: A 2. feladat 4. sorából milyen átalakítással jutottunk az 5. sorban szereplő képlethez? - szintén ide érdemes írni
Egy tipp az oldalhoz! - Add hozzá azokat a tantárgyakat a saját tárgyakhoz, melyeket aktuálisan hallgatsz a félév során. Így megkapod mások üzeneteit akik tantárggyal kapcsolatban írnak, illetve Te magad is írhatsz ezzel kapcsolatban. Írhatsz naptári bejegyzést, kitöltheted a tantárgyi adatlapját és egy tárgy lapján látod azokat a hallgatókat akik szintén felvették ebben a félévben a tárgyat.

Cimkefelhő

14 2004 2008 tavasz 3. előadás 4. óra algebra állampolgári ismeretek alternatív energiaforrások andorka arc assembly bencze beruházási függvény bikém civilizáció cserépedény deklaratív programozás dinamika dosztojevszkij durkheim élet évszámok fogyasztóvédelem gazdszoc géntech gépgyártás glikoneogenezis házi doga japán képzőművészet kéri bálint koaguláció középérték közigazgatás alapintézményei lineáris lorca magatartás matek jegyzet megtakarítás műanyag ökológia pcd példatár pr programozás szocioógia tereptan vám vetőmag word