Tartalmi kivonat
Bevezetés Ahhoz, hogy megértsük mit jelent az assembly nyelvű programozás, vissza kell kanyarodnunk a legelsô számítógépekhez. Ezen gépek processzorai a bináris számrendszer két számjegyének a 0 - nak és az 1 - nek megfelelô feszültségszinteket voltak képesek értelmezni, és az így kapott gépi kódú utasítást végrehajtani. Bármennyit is fejlôdött a számítástechnika azóta, a mai processzorok többsége is csak a digitális jeleket képesek értelmezni. ? A legelsô számítógépeket ennek megfelelôen kétállású kapcsolókkal programozták. A kapcsolók aktuális helyzete reprezentált egy nullákból és egyesekbôl álló bitmintát, amelyet a processzor a bitmintának megfelelô feszültségszintek alapján értelmezett és végrehajtott. Ez volt a gépi kódú programozás. Az elsô mikroprocesszorok megjelenésével, megjelentek az úgynevezett kit-ek, amely egy áramköri panel volt. A panel tartalmazta a processzort ( pl.: Intel 8080, 8
bites ), a processzor működéséhez nélkülözhetetlen áramköri elemeket, valamint egy hexadecimális billentyűzetet. Itt tehát a billentyűzet szolgálta azt a célt amit a hôskorszakban a kétállású kapcsolók, azaz a billentyűzet segítségével lehetett bevinni a processzor utasításainak gépi kódjait, amelyeket aztán végrehajtott a processzor. A gépi kódú programozásról elmondhatjuk tehát, hogy az teljes mértékben lefedi az illetô processzor utasításkészletét, azaz se többet se kevesebbet nem lehet vele megvalósítani, mint az utasításkészlet. A processzor utasításkészlete egyébként a processzor mikroprogram tárjában található, mikroprogram formájában. Ezen mikroprogram segítségével értelmezi és hajtja végre a processzor a gépi kódú utasításokat. Mint láthatjuk, a gépi kódú programozás esik legközelebb a számítógép hardverjéhez. Közkeletű nyelven szólva ez a “legalacsonyabb szintű” nyelv Az is
nyilvánvaló, hogy a programozásnak nem ez a legkényelmesebb módja. Ezért a fejlôdés során minden gépi kódnak egy rövid - többnyire 3 vagy 4 betűs - szimbólumsorozatot feleltettek meg. Ezeket a gépi kódú utasításokat reprezentáló karaktersorozatokat mnemonikoknak nevezzük. Ezekután a processzor programozása úgy történt, hogy - egy szövegszerkesztô segítségével - a gépi kódok helyett a nekik megfelelô mnemonikokat írtuk le. Ez a gépi nyelvű programozás máskeppen szólva az assembly nyelvű programozás. Ezzel kapcsolatban három lényeges dolgot kell megállapítanunk. Az elsô az az, hogy az assembly nyelv továbra is lefedi az illetô processzornak az utasítás készletét. Bevezetés A második az az, hogy - mivel a processzor továbbra is csak a gépi kódot képes értelmezni - kell egy szoftver eszköz, amely az assembly kódot a vele egyenértékű gépi kóddá alakítja. Ezt az eszközt assemblernek hívják Harmadik
megállapításunk pedig az, hogy - mivel az utasításkód is ugyanolyan bináris adat mint az utasítás valamely operandusa - a programunkban el kell különítenünk egy adat illetve egy kód területet. Késôbb szót ejtünk arról, hogy konkrétan melyik lesz az az assembler amit használni fogunk és arról is, hogy hogyan történik az adat illetve a kód terület elkülönítése. Az elôszóban említettem, hogy manapság is létjogosultsága van az assembly programozásnak bizonyos területeken, de nem kell az egész feladatot assembly-ben megoldani. Ez úgy lehetséges, hogy mind a PASCAL, mind a C nyelv képes az assembly-val történô egyittműködésre. Ebben a jegyzetben a C - assembly kapcsolatot tárgyalom majd. Bodlaki Tamás Az assembly nyelvű programozás alapjai Távoktatási jegyzet 10 Hiba! Nincs ilyen stílusú szöveg a dokumentumban. Impresszumoldal 3 Elôszó Elôszó Az elsô kérdés amire választ keresünk az az, hogy miért hasznos az
assembly nyelvű ( gépi nyelvű ) programozás alapjaival megismerkednünk, amikor olyan hatékony magasszintű programnyelvek állnak a rendelkezésünkre, mint a PASCAL vagy a C ill. a C++ nyelv A választ ma már fôleg a sebességviszonyok alakulása mentén kell keresnünk. Az alkalmazások egy jó részénél valóban nem számít mondjuk egy 10 ms - os idôintervallum. Egy munkaügyi információs rendszernél nem okoz tragédiát, ha egy lekérdezés eredménye 10 ms - mal késôb jelenik meg a képernyôn. Ugyanez a késés egy tárolt programvezérlésű telefonközpont működésénél már jelentôs, hiszen ahhoz hogy egy elôfizetô úgy érezze, hogy - miután felemelte a kézibeszélôt - azonnal tárcsahangot kap, pontosan 10 ms-os letapogatási idô szükséges. Bármilyen hatékony is egy magasszintű nyelv, a belôle készült tárgykód többnyire nem lesz olyan tömör, mint a problémát lefedô gépi nyelvű program, hiszen a magasszintű nyelvek fordítói
általában redundanciát tartalmazó tárgykódot készítenek, és nem használják ki a processzor teljes utasításkészletét. A válasz tehát úgy hangzik, hogy ott ahol nagyon kényesek a sebesség ill. idôzítési viszonyok, ott továbbra is létjogosultsága van a gépi nyelvű programozásnak. Manapság azonban nem a teljes programot kell gépi nyelven elkészíteni, hiszen az elôbb említett két nyelv lehetôvé teszi az assembly kapcsolatot. Ennek a lehetôségét ebben e jegyzetben is megvizsgáljuk. Meg kell említeni még, hogy egy hardware eszköz teljes kapacitását (sebesség, intelligencia stb.) is általában assembly nyelven tudjuk a leghatékonyabban kiaknázni. 10 Hiba! Nincs ilyen stílusú szöveg a dokumentumban. A jegyzet fô célja az assembly nyelvű programozás alapjainak bemutatása, gazdag példaprogram anyag segítségével. Mivel tehát nem egy konkrét processzor utasításkészletének bemutatása a fôcél, így elégségesnek bizonyult
az i8086 - os processzor utasításkészletének használata. A jegyzet írása során feltételeztem, hogy az Olvasó legalább egy magasszintű nyelvet ismer ( jó, ha ez a C nyelv ), illetve tisztában van a bináris és a hexadecimális számrendszer használatával. Az egyes fejezetek a következôképpen tagozódnak : Minden fejezet elején egy rövid összefoglaló található, amely tömören közli, hogy mit fog tartalmazni a fejezet. Ezt az összefoglaló részt a bekeretezett ∑ jelzi. Ezután következik a fejezet anyaga A példaprogramok ezzel a betűtípussal készültek, és keret veszi körül ôket. Minden fejezetet ellenôrzô kérdések és feladatok zárnak, melyeket érdemes megoldani már csak az alkotómunka kedvéért is ! A jegyzetben a következô témákat érintjük : Jelen elôszót egy bevezetés követi, amely definiálja a gépi kód, gépi nyelv fogalmakat, ill. elhelyezi az assembly nyelvet a programozási nyelvek között Az 1. fejezet az i8086-os
processzor memóriakezelésérôl ill regisztereirôl szól. Itt teszünk említést a programozáshoz nélkülözhetetlen software eszközökrôl is A 2 fejezet leírja az alapvetô direktívákat ill adattípus definíciókat egy egyszerű példaprogram kapcsán. Itt tárgyalunk néhány egyszerű DOS szolgáltatást is. A 3. fejezet az alapvetô aritmetikai utasításokat ismerteti A 4 fejezet a logikai utasítások leírását tartalmazza Az 5 fejezetben a feltételnélküli ill feltételes ugró utasításokat tárgyaljuk, míg a 6 fejezetben a ciklus szervezést A 7 fejezet tartalmazza az i8086 - os processzor memóri címzési módjainak leírását. A 8. fejezet a léptetô, rotáló és a flagekkel kapcsolatos utasításokat ismerteti A 9. fejezetben a szubrutinok szervezésérôl és használatáról lesz szó Itt tárgyaljuk röviden a makró használatot is. A 10 fejezet a stringutasításokat ismerteti valamint példákat közöl DOS, BIOS szolgáltatások használatára.
A 11. fejezet a C nyelv és az assembly nyelv kapcsolatát mutatja be inline módon, valamint önálló modulok formájában A 12 fejezet megemlít néhány különbséget az i8086 - os processzor és a fejletteb processzorok utasításai között 5 Elôszó A jegyzetben használt magyarázó ábrák, és jelentésük : ∑ ! ? : Fejezet elei összefoglaló : Fontos megállapítás : Kérdés vagy probléma jelzése : Egy téma részletes vizsgálata : Egy problémára megoldása Az ábrákat keret veszi körül. Mindegyik fejezetben tárgyalt anyag a 2x45 perces gyakorlat során átvehetô a hallgatókkal. Az ennél rövidebb fejezetek a gyakorlás lehetôségét biztosítják A féléves oktatás 15 hetes, a jegyzet 12 fejezetre oszlik. A fennmaradó 3 hétben lehetôség van a gyakorlásra és a számonkérésre ! Pécs, 1997. június 18 10 A Szerzô 1. Az i8086 memór iakezelése és r egiszter ei A fejezetben olyan mélységig lesz szó a processzor memória
kezelésérôl, amely a programozáshoz elengedhetetlenül szükséges. Azt nézzük meg, hogy hogyan lehet 16 bites regiszterekkel 20 bites címet elôállítani. A fejezet második része a processzor regisztereit tárgyalja, megemlítve a processzor működésében illetve programozásában betöltött szerepüket. Szó lesz még az assembly nyelvű programfejlesztés során alkalmazott software eszközökrôl. ∑ 1.1 M emór iakezelés A 8086 - os processzor minden 80x86-os processzor ôse. Regiszterei 16 bitesek, címsíne viszont 20 bites. Ennélfogva a címezhetô memória 1 MB Felmerül a kérdés, hogy hogyan lehet 16 bites regiszterekkel 20 bites címet elôállítani ? Nyilván egy regiszterrel sehogyan. A címképzésre két regisztert alkalmaznak Az egyik regiszter tartalmazza a 20 bites cím úgynevezett szegmens részét, a másik az offset részt. Ezáltal tehát a címet két 16 bites összetevôre osztottuk fel. Ez azt is jelenti, hogy ha egyszer rögzítettük a
szegmensregiszter tartalmát, akkor ehhez képest 64 KB memóriát tudunk címezni az offsetet tartalmazó regiszter segítségével. Ezekután a fizikai cím úgy adódik, hogy a szegmensregiszter tartalmát 16-tal szorozzuk, ami 4 helyiértékkel történô balra léptetést jelent a kettes számrendszerben. Az így kapott - immár 20 bites értékhez hozzáadjuk az offsetcímet Mivel a címeket leggyakrabban hexadecimális számrendszerben ábrázoljuk, ezért az elôbb elmondottak alapján a 16-tal való szorzás itt 1 helyiértékkel történô balra léptetést jelent, és az így kapott mennyiséghez kell hozzáadni az offsetet. Az általános képlet tehát : Fizikai cím = SZEGMENS* 16 + OFFSET Legyen például a szegmenscím 0040h, az offset 0002h. Ekkor a cím így irható fel : 0040:0002. Ebben az esetben a szegmenscímet 16-tal szorozva 0400h értéket kapunk. Ehhez hozzáadva a 0002h értéket 0402h fizikai cím adódik A h betű a hexa számrendszert jelöli. Ugyanez a
fizikai cím áll elô a 0030:0102 alakból ami azt jelenti, hogy a szegmensek átlapolhatják egymást. Ezt a jelenséget egy programon belül a szegmensregiszterek használatával védhetjük ki. ? 1. Az i8086 memór iakezelése és r egiszter ei A visszalakítás során az IBM ajánlást szokás követni. Legyen a fizikai cím 12345h. Ilyenkor a cím elsô jegyét leválasztjuk és kiegészítjük három 0-val : 12345h = 1000:2345 1.2 Az i8086-os r egiszter ei A regiszterek egy része ugyanolyan célra használható mint általában a központi memória, de mivel a processzoron belül helyezkednek el, ezért lényegesen gyorsabbak. A másik részük nélkülözhetetlen szerepet játszik a processzor működésében és programozásában. Az i8086-os regiszterei a következôk: AH AX AL BH BX BL CH CX CL DH DX DL SI DI BP SP IP F CS DS ES SS A regisztereket egyelôre vázlatosan ismertetem. Részletes ismertetésükre az adott funkciónál illetve feladatnál
kerül sor. Ezért ha most valami érthetetlennek tűnik az késôbb remélhetôleg világossá válik Az AX regiszter : Régebbi processzoroknál az akkumulátor regiszter nevet kapta kitüntetett szerepe miatt. Az i8086-nál ez a kitüntetett szerep csak részben maradt meg, mint azt a késôbbiekben látni fogjuk például a szorzásnál vagy az osztásnál. 12 1.2 Az i8086-os regiszterei Az AX regiszter ugyan 16 bites, de külön névvel lehet hivatkozni az alsó ill. felsô 8 bitjére. Az alsó byte-ra AL, a felsô byte-ra AH néven hivatkozhatunk Ennek a lehetôségnek az egyik oka nyilván a karakter műveletekben keresendô, hiszen egy karaktert 1 byte-on ábrázolunk. A BX regiszter : Kitüntetett szerepe lesz a memória címzésnél. A DS tartalmazza majd a változó szegmenscímét, BX pedig az offset címét Az AX-hez hasonlóan felezhetô A CX regiszter : A ciklus számláló szerepét tölti be, úgynevezett LOOP ciklus esetén. A két elôzô regiszterhez
hasonlóan felezhetô. Emellett általános célú regiszter is A DX regiszter : Periféria műveleteknél a periféria címét írjuk ebbe a regiszterbe. Egyes aritmetikai műveleteknél kiemelt szerepe van, például a szorzásnál és az osztásnál Ezenkívül használható általános célokra is Az utolsó felezhetô regiszter Az SI ( Source Index ) regiszter : A BX-hez hasonlóan memóriacímzéshez használható. Általában a forrásobjektum címét tartalmazza. Az SI mindig az adatszegmens regiszter-hez ( DS ) címzi a memóriát. Általános célokra is használható A DI ( Destination Index ) regiszter : Hasonló az SI regiszter szerepéhez, de általában a célobjektum címét tartalmazza. Szintén a DS - hez képest címzi a memóriát Szintén használható általános célokra is. A BP ( Basis Pointer ) regiszter : Amellett, hogy általános célú regiszter, kitüntetett szerepe van a verem (stack) címzésénél. Ilyenkor az SS regiszterhez képest címzi a
stack-et A stack aktuális elemének az offset címét tartalmazza. 13 1. Az i8086 memór iakezelése és r egiszter ei Az SP ( Stack Pointer ) regiszter : A stack “ tetejének” címzésére szolgál, azaz a stack aljától számított utolsó betöltött hely offset címét tartalmazza. Direkt módon tilos a megváltoztatása, egy esetet kivéve, de errôl majd késôbb. Az I P ( Instruction Pointer ) regiszter : A következô végrehajtandó utasítás offset címét tartalmazza. Direkt módon nem írható és nem olvasható ! Az F ( Flag ) regiszter : A flag regiszter a feltételbiteket ( flageket ) tartalmazza a következô kiosztásban : 15 14 13 12 11 10 9 X X X X O D 8 I T 7 S 6 Z 5 4 X A 3 2 1 0 X P X C A flagek nevei és ér telmezésük a következô : 14 0. Átvitel ( CARRY ) : Értéke 1 ha valamely művelet eredménye túllépi az ábrázolási tartományt. Például ha két 8 bites mennyiség összeadásánál fellép a 9 bit 1. X :
Kihasználatlan. 2. Párosság ( PARITY ) : Értéke egy, ha valamely művelet eredményében az 1. bitek száma páros 3. X : Kihasználatlan. 4. Közbülsô átvitel : (AUXILARY CARRY) Ha a 3. ill a 4 bit között átvitel keletkezik, akkor 1 az értéke. 5. X Kihasználatlan. : 1.2 Az i8086-os regiszterei 6. Nulla (ZERO) : Ha valamely művelet ereménye 0, akkor 1 az értéke, ellemkezô esetben 0. 7. Elôjel (SIGN) : Ha valamely művelet ereménye negatív, akkor 1 az értéke, ellenkezô esetben 0. 8. Nyomkövetés (TRACE) : 9. Megszakítás (INTERRUPT) Debuggereknél használatos lépésenkénti programvégrehajtásnál. A mi esetünkben nem lesz rá szükség. : Ha 1 az értéke, akkor engedélyezzük az interruptot, ha 0. akkor letiltjuk. 10. Irány (DIRECTION) : Ha 1 az értéke, akkor a stringműveletek a csökkenô indexek mentén hajtódnak végre, ha 0 akkor a növekvô indexek mentén hajtódnak végre. 11. Túlcsordulás (OVERFLOW) :
Értéke 1, ha valamely művelet során túlcsordulás lép fel. 12. X : Kihasználatlan. 13. X : Kihasználatlan. 14. X : Kihasználatlan. 15. X : Kihasználatlan. Természetesen a flagek működése most még áttekinthetetlennek tűnhet, de a konkrét utasításoknál egyértelművé válik majd a helyzet. A CS ( Code Segment ) regiszter : A soron következô utasítás szegmenscímét tartalmazza. Direkt módon nem írható és olvasható. Kezdeti értékét a DOS állítja be, a program betöltésekor 15 1. Az i8086 memór iakezelése és r egiszter ei Mint látható az utasítás teljes címe a CS:IP regiszterpárban van olymódon, hogy CS-ben van a szegmenscím, IP-ben van az offset cím. A DS ( Data Segment ) regiszter : A program adatszegmensének kezdôcímét tartalmazza. Kötelezôen nekünk kell beállítani a programunk elején. Minden változónk offset címe ettôl a tartalomtól számítódik, relatív módon. Igy a program elsô változójának
offset címe : 0. A regisztert konstans értékkel tilos feltölteni, csak regiszterbôl adhatunk át értéket ! Az ES (Extra Segment ) regiszter : Egy extra adatszegmens kezdôcímét tartalmazza. Ez egy 64 KB méretű memória terület A mi esetünkben string műveleteknél használjuk, de használható file bufferként és egyéb célokra is. Az SS ( Stack Segment ) regiszter : A stack szegmens kezdôcímét tartalmazza. A stack tetejének teljes címe tehát az SS:SP regiszterpárban található. Mivel a stack-et a BP regiszterrel címezzük relatív módon, így az elem teljes címe az SS:BP regiszterben található. A stack egyébként nem véletlenül kapta a verem elnevezést, hiszen amit legutoljára írunk bele azt olvassuk ki legelôször. Ez a L I FO ( Last In First Out ) típusú adatszerkezet. SP elem3 elem2 elem1 elem0 92 94 96 98 100 Ez a stack például ami az ábrán látható 100 byte méretű. Elôszôr az elem0-t, majd az elem1-et, elem2-ôt és végül az
elem3-at írtuk bele. Ekkor az SP regiszter értéke 92 lesz. A legelsô olvasás elem3-at fogja kiolvasni, így az SP új értéke 94 lesz. A stack többek között nélkülözhetetlen a késôb tárgyalandó szubrutin hívás megvalósításánál. 16 1.3 Az alkalmazott software eszközök 1.3 Az alkalmazott softwar e eszközök ! Nem célom az alkalmazott eszközök részletes bemutatása, hiszen midegyiknek van helpje. Csak annyira ismertetem a programokat most és a késôbbiekben, amennyire az nélkülözhetetlen a programfejlesztéshez ! Korábban volt róla szó, hogy szükségünk lesz egy olyan eszközre, amely az assembly nyelvű programunkat gépi kóddá fordítja le. Ennek a programnak általában assembler a neve. A jegyzetben szereplô összes programot a BORLAND C++ 3.1 programrendszer assemblerével fordítottam le Ennek a programnak a neve : TASM .EXE (Turbo Assembler) A TASM programnév nélkül elindítva kiírja a képernyôre a rövid helpjét ami
tartalmazza a kapcsolóit. Mi fôleg a /zi kapcsolókat fogjuk használni a nyomkövetés használatához. A TASM alapértelmezés szerint ASM kiterjesztésű állományt vár bemenetként. Szükségünk lesz egy olyan programra is ami a gépi kódú programból (.OBJ) futattható állományt szerkeszt (.EXE) Jelen rendszerben ez a TL I NK EXE (Turbo Linker) nevű program. Leggyakrabban a /v kapcsolóját fogjuk használni, lehetôvé téve a hibakeresést. Programnév nélkül elindítva szintén rövid helpet ír ki saját magáról. A TLINK alapértelmezés szerint .OBJ kiterjesztésű állományt vár bemenetként Ez a két program nélkülözhetetlen az assembly nyelvű programfejlesztéshez ! A TD.EXE ( Turbo Debugger ) nem nélkülözhetetlen ugyan, de rendkívül hasznos akkor, amikor a programunk nem működik kívánalmaink szerint, és hibát akarunk keresni ( és találni ) benne. A TD alapértelmezés szerint EXE állományt vár a bemenetén. Elindulása után
megjeleníti a forrásszöveget a képernyôn. Ezután a programot lépésenként is végre lehet hajtani úgy, hogy közben különbôzô változók értékeit figyeljük. A késôbbiek folyamán hasznos lehet a TL I B.EXE ( Turbo Librarian ) Ennek segítségével saját könyvtárat szerkeszthetünk ( .LIB ) meglévô object moduljainkból. Nyilván szükségünk van egy szövegszerkesztôre is. Ez lehet bármelyik ASCII szövegszerkesztô, például a Norton Commander editora. Ellenôr zô kér dések és feladatok 17 1. Az i8086 memór iakezelése és r egiszter ei 01. Mi a gépi kódú programozás, az assembly nyelvű programozás és mi az assembler ? 02. Hogyan írható fel a 25642 hexadecimális számrendszerben ? 03. Hogyan írható fel a 642 bináris számrendszerben ? 04. Hogyan írható fel az F6E3 decimális számrendszerben ? 05. Hogyan írható fel az F6E3 bináris számrendszerben ? 06. Hogyan írható fel a 0000011011100011 decimális számrendszerben
? 07. Hogyan írható fel a 0000011011100011 hexadecimális számrendszerben ? 08. Hogyan állítja elô az i8086-os a 20 bites címet ? 09. Melyek az i8086 regiszterei, és mi a szerepük ? 10. Melyek az i8086 jelzôbitjei, és mi a szerepük ? 11. Honnan kapta a stack a LIFO elnevezést ? 18 2. Egyszer űsített dir ektívák, fôbb adattípusok, alapvetô DOS szolgáltatások Σ Ebben a fejezetben elôször is különbséget teszünk utasítás és direktíva között, majd egy egyszerű assembly programocska kapcsán megismerkedünk az assembly program felépítésével és az egyszerűsített direktívákkal. Szó lesz az alkalmazható memóriamodellekrôl, az adatdefinícióról, illetve az alapvetô DOS szolgáltatások hívásáról, valamint az általunk használt alapvetô adattípusok definiálásáról. 2.1 A HEL L OASM ismer tetése Az egyszerű programocska a HELLO.ASM nevet viseli, és egy üzenetet ír ki a képernyôre. A program a következôképpen
néz ki : 1. sz példaprogram : ; ; Egyszerű assembly példaprogram ; .MODEL small .STACK 100h .DATA ; uzenet db Hajrá Ferencváros,13,10,$ ; .CODE mov ax, @data ; A DS regiszter feltöltése mov ds,ax ; Az adatszegmens kezdôcímével ; mov ah,9 ; Az üzenet kiiratása mov dx,OFFSET uzenet int 21h ; mov ah,4ch ; Visszatérés a DOS-hoz int 21h END 2.1 A HEL L OASM ismertetése Mielôtt rátérnénk a program működésének ismertetésére nézzük meg, hogy mi a különbség az utasítás és direktíva között. Az utasításból tényleges gépi kód keletkezik, amit a processzor végrehajt, míg a direktíva egy vezérlô információ a fordító számára, ami nem hajtódik végre ! ! Ezután nézzük a programot ! Az elsô három sor a ‘ ;’ karakterrel kezdôdik, ami programon belüli megjegyzést, magyarázó szöveget jelent. Az elsô direktívánk a MODEL direktíva, amely segítségével megadhatjuk, hogy milyen memória modellben akarunk dolgozni. Az
érvényes memóriamodellek egyébként a következôk : - tiny : Az adat és a kód ugyanazon a 64 KB-os memóriaterületen (szegmensben) helyezkedik el. - small : Az adat és a kód külön külön 64 KB szegmensben helyezkedik el. - medium : A kód lehet nagyobb mint 64 KB, de az adat nem. - compact : Az adat lehet nagyobb mint 64 KB, de a kód nem. - large : Mind az adat, mind a kód lehet nagyobb mint 64 KB, de egyetlen adattömb mérete sem lehet nagyobb mint 64 KB. - huge : Ugyanaz mint a large, de egy adattömb mérete lehet nagyobb mint 64 KB. A .M ODEL small kifejezés tehát azt jelenti, hogy mi a small modellt fogjuk használni. Ez a jegyzetben található összes mintapéldára igaz A .STACK 100h jelenti azt, hogy a programunk által használt stack méretét 100h azaz 256 byte-ban határozzuk meg. A stack-et változók tárolására, paraméterátadásra ill. szubrutinhívásra használja - többek között - a program 19 2. Egyszerűsített
direktívák, fôbb adattípusok, alapvetô DOS szolgáltatások Ezután történik meg az adat illetve a kód terület (adatszegmens, kódszegmens) szétválasztása. A DATA direktíva jelenti az adatterület kezdetét. Ezután a direktíva után definiálhatjuk adatainkat Hiába egyezik meg valamely adat a processzor valamelyik utasításának kódjával, akkor sem kerül végrehajtásra, mert az adatszegmensben foglal helyet. A fordító mindent adatnak értelmez, egészen a .CODE direktíváig Amit ezután írunk az vagy direktíva, vagy címke, vagy utasítás de semmiképpen sem adat. ! Az utolsó direktíva az END ami a mindenkori forrásszöveg végét jelzi. Az END direktíva után opcionálisan megadható egy címke, melynek segítségével meghatározhatjuk azt, hogy mely utasítástól kezdve induljon a programunk a betöltôdés után. Erre késôbb lesz szükségünk Ezekután tekintsük át programunk működését ! Az adatszegmensben definiáltunk egy uzenet nevű,
db típusú változót. A db típus a data byte rövidítése, és 8 bites adattípust jelent. A C nyelvben a char típusnak felelne meg. A mi esetünkben egy karaktervektort (karakterlánc, string) definiáltunk inicializálás segítségével. Az adatdefiniálásról késôbb bôvebben lesz szó. Az üzenet utolsó három byte-ja a 13,10,’ $’ kombináció Ebbôl a 13 az újsor, a 10 a kocsi vissza (kurzor a sor elejére) kódja és arra szolgálnak, hogy a kiiratás után a kurzort a következô sor kezdô poziciójára vezéreljék. A ‘ $’ jel a string kiíró DOS szolgáltatás számára jelzi a string végét. A .CODE direktíva utáni művelet az adatszegmensregiszter inicializálása A @data szimbólum tartalmazza az adatszegmensünk kezdetének szegmenscímét. Ezt a címet a mov utasítással írjuk be az ax regiszterbe Az elsô dolog amit tudnunk kell, hogy szegmensregiszterbe tilos közvetlenül konstans értéket írni, csak egy másik regiszterbôl tölthetünk fel
szegmensregisztert. A mov utasításról még lesz szó a továbbiakban. A mov utasítás az értékadó utasításhoz hasonlítható leginkább. Az elsô operendusa a cél operandus, a második a for r ás. A céloperandus felveszi a forrásoperandus értékét. Ennek értelmében az adatszegmens kezdôcímének szegmens része az ax regiszterbe íródik és onnan a ds regiszterbe kerül. ! Ezt a műveletet minden egyes program elején el kell végeznünk, különben a változóink kezdôcímét ( offset cím ) nem tudja mihez viszonyítani a fordító ! Most következik az üzenet kiiratása. Ezt a 9 számú DOS szolgáltatás segítségével fogjuk elvégezni A szolgáltatás úgy működik, hogy a sorszámát az ah regiszterbe kell írni, a kiiratandó string kezdô címét (offset címét) a dx regiszterben kell elhelyezni az OFFSET operátor segítségével. Ezután az int 21h utasítással hívjuk meg az ah regiszterben található sorszám alapján a kívánt DOS
szolgáltatást. Ez a DOS szolgáltatás tehát egy karakterláncot ír ki a 20 2.2 További DOS szolgáltatások ismertetése képernyôre. A karakterlánc kiirása a ‘ $’ jel hatására fejezôdik be Ha valamely string végérôl lehagynánk a ‘ $’ jelet, akkor a 9. számú DOS szolgáltatás addig folytatja a kiirást amíg valahol a memóriában nem talál egy ‘ $’ jelnek megfelelô kódot. Ez nem kívánt jelenségek elôfordulásához vezet ! Az üzenet kiiratása után egy dolgunk maradt : visszadni a vezérlést a DOSnak, egész konkrétan a COMAND.COM-nak Ezt a 4ch sorszámú DOS szolgáltatás végzi el A 4c után írt h betű jelentiazt, hogy a konstans hexadecimális számrendszerben értendô. A DOS szolgáltatás sorszámát most is az ah regiszterbe írjuk és magát a szolgáltatást az int 21h utasítással hívjuk meg ! A DOS-hoz való visszatérést mindig el kell végezni. Ennek elhagyása ki-számíthatatlan hibajelenségeket eredményez !
Programunk forrásszövegét az END direktíva zárja. 2.2 További DOS szolgáltatások ismer tetése A már megismert 9. illetve 4ch sorszámú DOS szolgáltatásokon kívül még két szolgáltatást fogunk gyakran használni. Ezek az 1 illetve a 2 sorszámú DOS szolgáltatások. Az 1. sorszámú DOS szolgáltatás egy karakter beolvasására alkalmas Az 1-et az ah regiszterbe kell írni, a szolgáltatást az int 21h utasítással kell meghívni. Ennek hatására programunk egy karakter billentyűzésére vár, majd ha ez megtörténik, akkor a billentyűzött karakter ASCII kódja az al regiszterbe kerül. Például : mov ah,1 int 21h mov cl,al ; Egy billentyű leütésére vár a program ; Az al-ben található karakterkódot ; beírjuk a cl regiszterbe. A 2. sorszámú DOS szolgáltatás pedig egy karakter - képernyôre történô kiiratására alkalmas A 2-ôt az ah regiszterbe, míg a kiiratni kívánt karakter kódját a dl regiszterbe kell írni. A kód helyett
írhatjuk magát a karktert is ‘ ’ jelek közé zárva. Ezt a DOS szolgáltatást is az int 21h utasítással hívjuk meg Például : mov ah,2 mov dl,65 int 21h ; A mov dl,’ A’ is tökéletesen megfelel ! 21 2. Egyszerűsített direktívák, fôbb adattípusok, alapvetô DOS szolgáltatások Ennek hatására a képernyôn - az aktuális kurzorpozicióban - egy A betű jelenik meg. Egy késôbbi példaprogramban ismertetem még a 2ah szolgáltatást. Ez a szolgáltatás az aktuális dátumot állítja elô Működése a következô : a 2ah - t itt is az ah regiszterbe kell írni, majd az int 21h utasítással hívjuk meg a szolgáltatást. Hatására a dátum a következôképpen áll rendelkezésünkre: cx regiszterben az év 4 számjegyen, dh regiszterben a hónap 2 számjegyen, dl regiszterben a nap 2 számjegyen és az al regiszterben a hét hányadik napja 1 számjegyen. A 0 a Vasárnapot jelenti, az 1 a Hétfôt stb 2.3 Alapvetô adattípusok és definiálásuk
Egy adattípussal már találkoztunk és db-nek hívtuk. Az assembly több adattípust ismer, de mi a db-n kívül a dw adattípussal foglalkozunk még A dw a data wor d rövidítése és 16 bites adattípust jelent. A C nyelv int típusa áll hozzá a legközelebb Az adatdefiniálásra a .DATA direktíva után kerül sor a követlkezô általános forma alapján : <címke> <típus> [<mennyiség>] [<inicializálás>] A <címke> jelenti az adatunk nevét. Betűvel kell kezdôdnie, számjegy és az ‘ ’ ( aláhúzás) karakter szerepelhet benne. Az elsô 32 karaktert különbözteti meg az assembler. Mivel a címkét adatdefiníció követi, igy tilos a ‘ :’ használata a cimke után. Más esetekben ez kötelezô ( lásd késôbb ) A <cimke> használata kötelezô ! A <típus> jelenti az adatunk típusát, ami a mi esetünkben db vagy dw lehet. Használata szintén kötelezô ! Példa az eddig elmondottakra : .DATA karakter index . db
dw ; karakter nevű 1 byte-os változó ; index nevű 2 byte-os változó Abban az esetben, ha nem egyszerű változóra van szükségünk, hanem vektorra, vagy mátrixra akkor a mennyiséget is meg kell adnunk, vagy inicializálásal 22 Ellenôr zô kér dések és feladatok kell közölnünk az assemblyvel hogy nem egyszerű változóról van szó ! Inicializálni természetesen egyszerű változót is lehet. Például : kar db ‘ A’ szoveg db 20 dup(32) szoveg1 db 10 dup(?) vektor dw 9 dup(0) matrix dw dw dw 1, 2, 3 4, 5, 6 7, 8, 9 ; kar nevű 1 byte-os változó, kezdeti értéke ; ‘ A’ ; szoveg nevű 20 byte-os változó, mind; egyik byte kezdeti értéke a szóköz ; szoveg1 nevű 10 elemű karakter típusú ; vektor, kezdeti érték határozatlan ; vektor nevű 9 elemű integer változó, mind; egyik elem értéke 0 ; matrix nevű 3x3 - as integer típusú ; mátrix, inizializálva A dup operátor láthatóan arra alkalmas, hogy az elôtte álló számú
adott típusú adategységnek ( byte vagy word ) a zárójelek közötti kezdôértéket adja. Ha a kezdôérték közömbös számunkra, akkor a ‘ ?’ karaktert használjuk. Attól, hogy a matr ix nevű változó 3 sorban illetve 3 oszlopban lett inicializálva, attól még ugyanúgy sorfolytonosan tárolódnak az elemei, mintha azt írtuk volna, hogy : matr ix dw 1,2,3,4,5,6,7,8,9. Azt, hogy vektorként vagy mátrixként fogjuk kezelni, azt majd az indexelés módja fogja eldönteni ! Bármely - az adatszegmensben deiniált - egyszerű változóra a ‘ [‘ ’ ]’ zárójelpár segítségével hivatkozhatunk a kód szegmensben. Példa : mov ax,[ertek1] ; Ha ertek1 3 volt akkor az ax értéke is 3 lesz Amennyiben konstans értéket akarunk használni, úgy a következôket kell tudnunk : a konstans után írt ‘ b’ azt jelenti, hogy bináris konstansról van szó, a ‘ h’ hexadecimális, az ‘ o’ pedig oktális konstanst jelöl. Amennyiben a konstans értéket nem
követi betűjel, úgy decimális konstansról van szó. Hexadecimális konstans esetén elôfordul, hogy a konstans betűvel kezdôdik. Ilyenkor egy ‘ 0’ karaktert kell eléje tenni, különben a fordító címkehivatkozásnak tekinti ! Példák konstansdefiníciókra : hexconst binconst db 0aeh db 00011001b Ellenôr zô kér dések és feladatok 23 ! 2. Egyszerűsített direktívák, fôbb adattípusok, alapvetô DOS szolgáltatások 01. Mi a különbség az utasítás és a direktíva között ? 02. Miért szükséges az adat ill. a kódszegmens megkülönböztetése egy programon belül ? 03. Mi a szerepe a .MODEL direktívának ? 04. Hogyan adhatjuk meg egy programon belül az általunk használni kívánt stack terület méretét ? 05. Miért kötelezô az adatszegmens regiszter inicializálása ? 06. Mire használjuk az OFFSET operátort ? 07. Általában hogyan tudunk elérni egy DOS szolgáltatást, konkrétan milyen DOS szolgáltatásokat ismer ?
08. Mi az adatdefiníció általános alakja ? ( Rövid magyarázattal ! ) 09. Mi a különbség a db és a dw adattípus között ? 10. Hogyan definiálhatunk vektorokat, mátrixokat ? 11. Készítsen egy olyan programot amely beolvas egy karaktert a klaviatúráról, majd visszaírja azt a képernyôre ! 12. Készítsen egy olyan programot amely kiír egy üzenetet a képernyôre, majd egy soremelést ír ki, utána pedig még egy üzenetet ír ki a képernyôre ! 24 3. Ar itmetikai utasítások Σ Ebben a fejezetben komoly lépéseket teszünk annak érdekében, hogy az elôzô fejezetben látott példa programnál bonyolultabb programokat is tudjunk írni. Eôször a MOV értékadó utasítással, majd a fôbb aritmetikai utasításokkal ismerkedünk meg. Ezt követôen konverziós illetve egyéb utasítások tárgyalása következik. 3.1 Az i8086-os pr ocesszor általános utasítás for mátuma Mielôtt rátérnénk a konkrét uatsítások tárgyalására,
vizsgáljuk meg a processzor utasítás formátumát általában: [<címke>] <utasítás | dir ektíva > [<oper andusok>] [<; megj egyzés>] Mint látható, az utasítás formátum négy részbôl áll, ebbôl egy kötelezô, három pedig opcionális. A részek leírása a következô : [<címke>] : Hasonló az adatdefiníciónál megismert címkéhez de itt a ‘ :’ karakter követi. A ‘ :’ karaktert kell használni egy címke után, ha az önállóan szerepel egy sorban, vagy utasítás követi. <utasítás | direktíva > : Azok az utasítások amiket nemsokára részletezünk, vagy a már megismert direktívák valamelyike. Ez a kötelezô rész ! [<operandusok>] : Az operandusok mindig utasításokhoz tartoznak. Számuk lehet 0, 1, vagy több Az operandus lehet regiszter, memóriaváltozó, indexkifejezés vagy konstans. [<;megjegyzés>] : Magyarázó szöveg az utasítás sor végén. Példa : mov cx,10 3.2 A
MOV utasítás beolvas: mov ah,1 int 21h ; Itt kezdôdik a beolvasás . 3.2 A M OV utasítás Szintaxis : MOV <op1>,<op2> A mov utasítás értékadásra szolgál. Az <op1> felveszi <op2> értékét, <op2> értéke változatlan marad. Az utasítás működését az <op1> = <op2> formával lehet a legrövidebben leírni, ahol az ‘ =‘ karakter jelentése az, hogy “ legyen egyenlô” . <op1> -et általában cél- , míg <op2>-ôt forrás operandusnak nevezik. A forrásoperandus lehet : általános célú regiszter, memóriacím, konstans, indexkifejezés. A céloperandus általános célú regiszter illetve memóriacím lehet. A mov utasítással kapcsolatban tilos a következôket megtenni ! - ! A kódszegmens regiszternek ( CS ) értéket adni. Szegmensregiszterbôl szegmensregiszterbe írni. Konstans értéket szegmensregiszterbe írni. Különbözô típusú operandusokat használni. Ezeken kívül nem fordulhat
elô az az eset, hogy mindkét operandus memória változó ! Példák hibás mov utasításokra : mov ax,bl ; Különböznek az operandus típusok ! mov [szam1],[szam2] ; Mindkét operandus memória változó ! mov ds,@data ; Szegmensregiszterbe nem írhatunk konstanst ! 3.3 Az ADD és ADC utasítások Szintaxis : ADD <op1>,<op2> , ADC <op1>,<op2> 25 3. Aritmetikai utasítások Az add utasítás a két operandusának összeadására szolgál. Hasonlóan a mov utasításhoz itt is <op1> a cél- és <op2> a forrás operandus. Megfelel az <op1> = <op1> + <op2> műveletnek. A két operandus típusa meg kell egyezzen, valamint itt is érvényes az a megkötés, hogy nem lehet egyszerre mindkét operandus memóriaváltozó. Konstans érték csak a forrás operandus lehet. Az utasítás a következô flageket érinti : - CF = 1 ha átvitel keletkezett. ( Például két 8 bites mennyiség összege már nem
ábrázolható 8 biten, hanem megjelenik a 9. bit ) - PF = 1 ha az eredmény alsó byte-jában az 1. bitek száma páros. - AF = 1 ha az eredmény 3. és a 4 számú bitje között átvitel keletkezett. ( A bitek számozása 0 - tól indul ! ) - ZF = 1 ha az összeadás eredménye 0. - SF = 1 ha az eredmény negatív. - OF = 1 ha túlcsordulás lép fel. Ezek után nézzük meg, hogy mikor kell használnunk az adc utasítást ! Abban az esetben ha 32 bites mennyiségeket akarunk összeadni elôfordulhat, hogy az alsó 16 bit összeadásakor átvitel keletkezik. Ekkor a CF értéke 1 lesz Világos, hogy a felsô 16 bit összeadásánál ezt a bitet is figyelembe kell venni, tehát a sima add utasítást nem használhatjuk. Ilyenkor az adc használatára kerül sor ! Az adc utasítás az <op1> = <op1> + <op2> + [CF] műveletnek felel meg. Az utasítás ugyanazokat a flageket érinti mint az add. Példák : 26 mov al,10 mov bl,100 add al,bl ; al
értéke 110 lesz ; mov cx, 100 mov bx, 200 mov dx, 100 mov ax, 200 add ax,bx ; ax értéke 400 lesz adc dx,cx ; dx értéke 200 lesz ; a dx:ax regiszterpárban keletkezett az eredmény 3.4 A SUB és SBB utasítások 3.4 A SUB és SBB utasítások Szintaxis : SUB <op1>,<op2> , SBB <op1>,<op2> A sub utasítás a két operandusának kivonására szolgál. Megfelel az <op1> = <op1> - <op2> műveletnek. Ugyanazok a megkötések érvényesek a két operandusra mint az add utasitás esetén, és ugyanazokat a flageket érinti a sub mint az add. Itt is szembekerülünk azzal a problémával, hogy a kivonás eredménye nem ábrázolható az adott tartományon, mert alulcsordulás keletkezik. Ez akkor fordulhat elô, ha nagyobb a kivonandó, mint a kisebbítendô. Ilyenkor a CF értéke 1 lesz. 32 bites mennyiségek kivonása esetén tehát a felsô 16 bit kivonásánál CF értékét is figyelembe kell venni. Ezt végzi az sbb utasítás :
<op1> = <op1> - <op2> - [CF] Példák : mov al,10 mov bl,100 sub bl,al ; mov cx, 100 mov bx, 100 mov dx, 200 mov ax, 200 sub ax,bx sbb dx,cx ; bl értéke 90 lesz ; ax értéke 100 lesz ; dx értéke 100 lesz ; a dx:ax regiszterpárban keletkezett az eredmény 3.5 A CM P utasítás Szintaxis : CMP <op1>,<op2> A cmp utasítás ugyanúgy működik mint a sub, azzal a nagy különbséggel, hogy <op1> értékét nem írja felül a kivonás eredményével, hanem csak a flageket állítja be úgy mintha kivonás történt volna. Feltételes ugró utasítások elôtt kiválóan használható feltétel vizsgálat céljára. Az <op1> és <op2> típusa meg kell egyezzen, konstans csak <op2> lehet. Nem lehet egyszerre mindkét operandus memória változó. Példa : mov ah,1 27 3. Aritmetikai utasítások int 21h cmp al,13 ; <ENTER> - t billentyűztünk ? 3.6 Az I NC utasítás Szintaxis : INC <op> Az inc
utasítás eggyel növeli operandusának értékét. Az elôzô utasításokkal ellentétben a CF -et nem érinti, így az átvitel tényét nem tudjuk lekérdezni. Amennyiben ez gondot jelent, úgy az add <op>,1 utasítást kell használni ! Példa : mov al,9 inc al ; al értéke 10 lesz 3.7 A DEC utasítás Szintaxis : DEC <op> Az dec utasítás eggyel csökkenti operandusának értékét. Az elôzô utasításokkal ellentétben a CF -et nem érinti, így az átvitel tényét nem tudjuk lekérdezni Amennyiben ez gondot jelent, úgy a sub <op>,1 utasítást kell használni ! Példa : mov al,11 dec al ; al értéke 10 lesz 3.8 A NEG utasítás Szintaxis : NEG <op> A neg utasítás az operandusának a kettes komplemensét állítja elô, azaz -1 gyel való szorzást hajt végre az operanduson. A művelet a CF,PF,AF,ZF,SF és az OF flageket érinti. Példa : mov al,10 neg al ; al értéke -10 lesz 3.9 A CBW és CWD utasítások 28 3.10 A MUL
és IMUL utasítások Szintaxis : CWB , CWD Ez a két utasítás nagyon hasznos szerepet játszik az aritmetikai műveleteknél. A cbw utasítás az al regiszterben lévô elôjeles byte-ot elôjeles szóvá konvertálja és az ax regiszterbe helyezi, míg a cwd utasítás az ax regiszterben lévô elôjeles szót elôjeles duplaszóvá konvertálja és a dx:ax regiszterpárba helyezi. A most tárgyalásra kerülô szorzásnál és osztásnál jelentôs szerepet kaphat ez a két utasítás ! Példa : mov al,10 neg al cbw ; mov ax,100 neg ax cwd ; al = 10 ; al = -10 ; ax = -10 ; ax = 100 ; ax = -100 ; dx:ax = -100 3.10 A M UL és I M UL utasítások Szintaxis : MUL <op> , IMUL <op> A mul utasítás elôjelnélküli mennyiségek szorzására szolgál, és két esetet képes kezelni : egyrészt 8 bitet szoroz 8 bittel, másrészt 16 bitet 16 bittel. Az elsô esetben az egyik operandus szigorúan az al regiszterben kell, hogy elhelyezkedjen, a másik operandus
valamelyik 8 bites általános célú regiszterben, vagy memóriaváltozóban foglal helyet. Az eredmény az ax regiszterben keletkezik a szorzás elvégzése után. Példa : mov al,25 mov bl,4 mul bl ; az ax értéke 100 lesz A másik esetben az egyik operandus az ax regiszterben kell, hogy helyet foglaljon, a másik pedig valamely 16 bites általános célú regiszterben, vagy memóriaváltozóban foglal helyet. Az eredmény a dx:ax regiszterpárban keletkezik. Az alsó szó az ax, a felsô a dx regiszterben lesz elérhetô Az OF értéke 1 lesz, ha a szorzás után az eredmény felsô fele 0-tól eltérô, egyébként törlôdik. A CF az OF-fel megegyezôen változik, a többi flag értéke nem meghatározott. 29 3. Aritmetikai utasítások Az imul utasítás két elôjeles mennyiséget szoroz össze. A műveleti szélesség itt is kétféle lehet, mint a mul-nál, az operandusok elhelyezkedése is tökéletesen megegyezik az elôzôekben leírtakkal. 3.11 A DI V és I DI V
utasítások Szintaxis : DIV <op> , IDIV <op> A div utasítás elôjelnélküli mennyiségek osztására szolgál, és szintén két esetet képes kezelni : egyrészt 16 bitet oszt 8 bittel, másrészt 32 bitet 16 bittel. Az elsô esetben az osztandó szigorúan az ax regiszterben kell, hogy elhelyezkedjen, az osztó valamelyik 8 bites általános célú regiszterben, vagy memóriaváltozóban foglal helyet. Az osztás elvégzése után az eredmény az al regiszterben keletkezik, a maradék pedig az ah-ban. Példa : mov ax,41 mov bl,20 div bl ; az al értéke 2 az ah értéke 1 lesz. A másik esetben az osztandó a dx:ax regiszterpárban kell, hogy helyet foglaljon, a másik pedig valamely 16 bites általános célú regiszterben, vagy memóriaváltozóban foglal helyet. Az eredmény az ax regiszterben keletkezik, a maradék a dx regiszterben lesz elérhetô. Egyik flag értéke sem meghatározott. Amennyiben az eredmény nem fér el a célregiszterben ( al vagy ax
) vagy az osztó 0, úgy a 0. megszakítás lép életbe ami a 0-val való osztást kezeli. Az idiv utasítás teljesen megegyezôen működik a div-vel, csakhogy itt az osztandó is és az osztó is elôjeles mennyiség. Abban az esetben ha két 16 bites elôjeles mennyiséget akarunk elosztani, átalakításhoz kell folyamodnunk, mert ilyen alakot az idiv nem ismer. Ebben az esetben az osztandót beírjuk az ax regiszterbe, majd a cwd utasítást alkalmazzuk. Ennek hatására a dx:ax regiszterpárban foglal helyet az osztandónk, így semmi akadálya az osztásnak Két 8 bites mennyiség esetén hasonlóan járunk el csak nem a cwd, hanem a cbw utasítást használjuk ! Példa : 30 ; bx - ben -200 van ; cx - ben 100 van mov ax,bx cwd ; a dx:ax-ben lesz a -200 3.12 Az XCHG utasítás div cx ; ax-ben -2 lesz, dx-ben 0 3.12 Az XCHG utasítás Szintaxis : XCHG <op1>,<op2> Az xchg utasítás felcseréli két operandusának az értékét. A két operandus nem lehet
egyszerre memóriaváltozó, és típusuk megegyezô kell legyen. Az utasítás nem érinti a flageket. Példa : mov al,1 mov ah,0 ; így az ax-ben 1 van xchg al,ah ; al és ah megcserélôdik, az ax-ben 256 lesz. 3.13 Az OFFSET oper átor Szintaxis : OFFSET <memóriaváltozó> Bár nem utasítás, de elég gyakran kell majd használnunk. Az offset operátor az utána álló memória változó kezdôcímét állítja elô. A kezdôcím alatt a szegmens kezdethez ( ds regiszter tartalma) képesti offset címet kell érteni. Példa : mov dx,OFFSET szoveg ; dx-be kerül a szoveg kezdôcíme Ellenôr zô kér dések és feladatok 01. Ismertesse az i8086-os általános utasításformátumát, és annak elemeit ! 02. Hogyan működik a MOV utasítás, milyen megkötések vonatkoznak rá ? 03. Adja össze a dx:ax illetve a cx:bx regiszterpárok tartalmát ! 04. Vonja ki a dx:ax regiszterpár tartalmából a cx:bx regiszterpár tartalmát! 31 3. Aritmetikai
utasítások 05. Mi az azonosság illetve a különbség a SUB és a CMP utasítás működését tekintve ? 06. Mi a különbség az add al,1 és az inc al utasítások között ? 07. Mi a különbség a sub bl,1 és az dec bl utasítások között ? 08. Hogy lehet a -25 - öt elôjeles duplaszóvá konvertálni ? 09. Végezze el a következô szorzásokat : 30* 20, 300 200, 3000 2000, 30000* 20000 ! 10. Végezze el a következô szorzásokat : -30* 20, -300 200, -3000 2000, -30000* 20000 ! 11. Végezze el a következô osztásokat : 16/9, 160/60, 1600/700, 16000/1500, 160000/9, 1600000/13400 ! 12. Végezze el a következô osztásokat : -16/9, -160/60, -1600/700, -16000/1500, -160000/9, -1600000/13400 ! 13. Az ax regiszter tartalma 2. Alakítsa át 512-vé úgy, hogy nem alkalmazhatja sem a mov sem az add, sem a mul utasításokat ! 14. Melyik általunk ismert DOS szolgáltatásnál kell használni az OFFSET operátort ? 32 4. L ogikai utasítások Σ Ebben
a fejezetben a logikai utasításokat mutatom be. Feltételezem, hogy a tárgyalandó logikai utasítások igazságtáblájával tisztában van az olvasó ! 4.1 Az AND és a TEST utasítások Szintaxis : AND <op1>,<op2> , TEST <op1>,<op2> Az and utasítás bitenkénti ÉS műveletet végez a két operandusa között, és ennek a műveletnek az eredményét az elsô operandusban tárolja. A működése tehát a következôképpen írható le : <op1> = <op1> ÉS <op2>. Az utasítás a PF, ZF és SF flageket érinti. Itt is érvényesek a szokásos megkötések : az operandusok típusai meg kell, hogy egyezzenek, valamint nem lehet két memóriaváltozón elvégezni az ÉS műveletet ! ! A test utasítás működése teljesen hasonló az and működéséhez. A különbség abban van, hogy test nem rombolja le az <op1> értékét az eredménnyel, hanem csak a flageket állítja be az ÉS műveletnek megfelelôen. Az and utasítás
több mindenre használható. Elôször is bármely bitminta bármely bitjét 0-ba tudjuk “ kényszeríteni” , ha a bitmintát egy olyan bitmintával hozzuk ÉS kapcsolatba, amelynek a megfelelô pozicióján 0 áll, a többi pozición pedig 1. Példa : mov al,00110011b ; a 4. bitet 0-ba akarom állítani, a bitek ; sorszámozása 0-tól indul ! and al,11101111b ; az eredmény 00100011 lesz. Az and utasítás másik hasznos alkalmazása a “ maszkolás” nevet viseli. Ez azt jelenti, hogy egy ismeretlen értékű bit vagy bitcsoport értékét meg tudjuk határozni olymódon, hogy a kérdéses poziciókon 1-gyel, a többi pozición 0-val végzünk ÉS műveletet. A maszkolás eredményétôl függôen tudjuk tovább folyatni a programunkat. A maszkolást a 2 sz példaprogram mutatja be Egy 8 4.2 Az OR utasítás bites minta 3. 4 ill 5 számú bitjének az értékét kell megállapítanunk Mivel három bitrôl van szó, ez az érték elvileg lehet 111, 110, 101, 011, 100,
010, 001 és 000. Nyilván mindegyik érték különbözô információt hordoz 2. sz példaprogram : .MODEL small .STACK 100h .DATA ; minta db 00101010b info db 28h ; 00101000b ( erre vadászunk ! ) ; .CODE mov ax, @data mov ds,ax ; mov ax,0 mov al,[minta] ; A gyakorlatban valamilyen ; periféria küldi. and al,00111000b ; Itt történt meg a maszkolás ! cmp al,[info] ; ; Az összehasonlítás eredményétôl függô ; program sorok ; mov ah,4ch int 21h END Az and utasítás még arra is alkalmas, hogy egy regiszterrôl a leggyorsabb módon eldöntsük, hogy tartalma 0. Példa : and ax,ax ; Ha 0 volt az ax-ben akkor ZF értéke 1 lesz. 4.2 Az OR utasítás Szintaxis : OR <op1>,<op2> 33 4. Logikai utasítások Az or utasítás bitenkénti VAGY műveletet végez a két operandusa között, és ennek a műveletnek az eredményét az elsô operandusban tárolja. A működése tehát a következôképpen írható le : <op1> = <op1> VAGY <op2>. Az
utasítás szintén a PF, ZF és SF flageket érinti. Itt is érvényesek a szokásos megkötések : az operandusok típusai meg kell, hogy egyezzenek, valamint nem lehet két memóriaváltozón elvégezni a VAGY műveletet ! Az or utasítás segítségével bármely bitminta bármely bitjét 1-be tudjuk “ kényszeríteni” , ha a bitmintát egy olyan bitmintával hozzuk VAGY kapcsolatba, amelynek a megfelelô pozicióján 1 áll, a többi pozición pedig 0. Példa : mov al,00100011b ; a 4. bitet 1-be akarom állítani, a bitek ; sorszámozása 0-tól indul ! or al,00010000b ; az eredmény 00110011 lesz. 4.3 Az XOR utasítás Szintaxis : XOR <op1>,<op2> Az xor utasítás bitenkénti KIZÁRÓ VAGY műveletet végez a két operandusa között, és ennek a műveletnek az eredményét az elsô operandusban tárolja. A működése tehát a következôképpen írható le : <op1> = <op1> KIZÁRÓ VAGY <op2>. Az utasítás ugyancsak a PF, ZF és SF
flageket érinti Itt is érvényesek a szokásos megkötések : az operandusok típusai meg kell, hogy egyezzenek, valamint nem lehet két memóriaváltozón elvégezni a KIZÁRÓ VAGY műveletet ! Az xor utasítás segítségével bármely bitminta bármely bitjét az ellenkezô értékébe tudjuk “ kényszeríteni” , ha a bitmintát egy olyan bitmintával hozzuk KIZÁRÓ VAGY kapcsolatba, amelynek a megfelelô pozicióján 1 áll, a többi pozición pedig 0. Ezt a műveletet bit komplementálásnak hívjuk Példa : mov al,00110011b ; a 4. bitet akarom komplementálni ; sorszámozása 0-tól indul ! ; xor al,00010000b ; az eredmény 00100011 lesz. Az xor utasítás másik alkalmazása : egy regiszter kinullázásának ez a leggyorsabb módja. 34 4.4 A NOT utasítás Példa : xor ax,ax ; ax-ben 0 lesz 4.4 A NOT utasítás Szintaxis : NOT <op> A not utasítás az operandusának az egyes komplemensét állítja elô, azaz mindegyik bitjének értékét az
ellenkezôjére változtatja. Egyik flagre sincs hatással. Ellenôr zô kér dések és feladatok 01. Mi a különbség az and és a test utasítások között ? 02. Mire használható az and utasítás ? 03. Milyen eljárással állapíthatjuk meg egy bitminta adott bitcsoportjának az értékét ? 04. Állítsa 0 értékűre az alábbi byte 2. és 3 bitjeit A sorszámozás 0-tól indul : 00101101b ! 05. A dl regiszterben egy ismeretlen bitminta található. Melyik bitmintával kell maszkolni a dl tartalmát ahhoz, hogy megállapítsuk a 4., 5, 6 bitek értékét ? 06. Hogyan működik az or utasítás, milyen megkötések érvényesek az operandusaira ? 07. Állítsa 1 értékűre az alábbi byte 3. és 4 bitjeit A sorszámozás 0-tól indul : 00100101b ! Milyen bitmintát kell használnunk ? 08. Hogyan működik az xor utasítás, milyen megkötések érvényesek az operandusaira ? 09. Komplementálja az alábbi byte 4. bitjét A sorszámozás 0-tól indul :
00100101b ! 10. Mire használhatjuk a not utasítást ? 35 4. Logikai utasítások 36 5. Feltételnélküli és feltételes ugr ó utasítások Σ Eljutottunk arra a fokra, hogy egyszerű adatokon adatmozgatást, aritmetikai és logikai műveleteket tudunk végrehajtani. Ez az állapot csak program szekvenciák elôállítását teszi lehetôvé, vagyis utasítások egymásutánjának a végrehajtását. Ahhoz, hogy egy feltétel szerint el tudjunk ágazni két vagy több irányba - azaz szelekciót tudjunk végrehajtani - , meg kell ismerkednünk a feltételnélküli és feltételes ugró utasításokkal. Ebben a fejezetben a feltételnélküli és feltételes ugróutasításokat tárgyaljuk. 5.1 Feltételnélküli ugr ó utasítás : j mp Szintaxis : JMP <cimke> Az j mp utasítás az ôt követô cimkére adja át a vezérlést, tehát a programunk nem a következô utasításon, hanem a címkéhez tartozó utasításon folytatódik. Magas szintű nyelveken a j
mp megfelel a sokak által szidott goto utasításnak. Mint látni fogjuk néhány esetben elkerülhetetlen a használata. ! Amennyiben a cimke az ugrás helyétôl egy elôjeles byte-nyi távolságra van ( -127 ill. +128 byte ), ebben az esetben a j mp utasítás relatív cimzést használ Ez azt jelenti, hogy az ugrás távolsága elôjelesen hozzáadódik az IP regiszter tartalmához, ezáltal megvalósul a vezérlésátadás. Abban az esetben ha az ugrás helye távolabb van mint amit egy elôjeles byteon ábrázolni tudunk, abszolút cimzéssel hajtódik végre a j mp. Ez pedig azt jelenti, hogy a cimkéhez tartozó offset cím kerül az IP regiszterbe, azaz símán felülíródik az IP regiszter tartalma. A címkéhez tartozó offset cím a szimbólumtábla alapján érhetô el. Amennyiben a programozó biztosan tudja, hogy az ugrás távolsága belül esik a -127 ill. +128 byte-os határon úgy a shor t operátort alkamazva, direkt módon elôírhatja a relatív címzéssel
végrehajtott ugrást. Példa : jmp short cimke1 ; a program cimke1-en folytatódik 5.2 Feltételes ugró utasítások ; a jmp végrehajtása relatív cimzéssel ; történik 5.2 Feltételes ugr ó utasítások A feltételes ugró utasítások használata rendkívül megnöveli programunk intelligenciáját, hiszen segítségükkel feltételtôl függô elágazásokat ( szelekciókat ) tudunk megvalósítani. A feltételes ugró utasításoknak három csoportja van. Mindhárom csoport relatív címzést használ, vagyis a feltételes ugró utasításokkal -127 vagy +128 byte-ot tudunk ugrani. A - elôjel az ugrás szempontjából a visszafelé, a + elôjel az elôre irányt jelenti. A viszonyítási alap mindig az a hely ahol az ugrási parancsot kiadtuk ( az ugrás helye ). Amennyiben nagyobb távolságra akarunk ugratni, akkor természetesen gondoskodnunk kell a megoldásról. Ezt a késôbbiek során tárgyalom 5.21 Elôjel nélküli mennyiségekkel kapcsolatos
utasítások Az ugró utasítások használata elôt természetesen szükség van egy összehasonlító műveletre. Ez a mi esetünkben a cmp utasítás lesz Abban az esetben, ha a következô összehasonlítást végezzük el : cmp op1,op2 ; op1 és op2 elôjelnélküli mennyiségek akkor a következô feltételes ugró utasításokat használhatjuk : ja jb je jae jbe cimke cimke cimke cimke cimke ; ugrás ha op1 > op2 ; ugrás ha op1 < op2 ; ugrás ha op1 = op2 ; ugrás ha op1 >= op2 ; ugrás ha op1 <= op2 Érvényes ennek az öt esetnek a tagadása is : jna jnb jne jnae jnbe cimke cimke cimke cimke cimke ; ugrás ha op1 <= op2 ; ugrás ha op1 >= op2 ; ugrás ha op1 != op2 ; ugrás ha op1 < op2 ; ugrás ha op1 > op2 37 5. Feltételnélküli és feltételes ugró utasítások Látható, hogy a rendelkezésre álló alakok rendundanciát tartalmaznak. A program környezetétôl függôen viszont, az egyik alak jobban kiemelheti a lényeget, mint a
vele megegyezô működésű másik. Példa : cmp al,20 ja ide ; al-ben 30 van ; a program az ide cimkén folytatódik 5.22 Elôjeles mennyiségekkel kapcsolatos utasítások Abban az esetben, ha a következô összehasonlítást végezzük el : cmp op1,op2 ; op1 és op2 elôjeles mennyiségek akkor a következô feltételes ugró utasításokat használhatjuk : jg jl je jge jle cimke cimke cimke cimke cimke ; ugrás ha op1 > op2 ; ugrás ha op1 < op2 ; ugrás ha op1 = op2 ; ugrás ha op1 >= op2 ; ugrás ha op1 <= op2 Érvényes ennek az öt esetnek a tagadása is : jng jnl jne jnge jnle cimke cimke cimke cimke cimke ; ugrás ha op1 <= op2 ; ugrás ha op1 >= op2 ; ugrás ha op1 != op2 ; ugrás ha op1 < op2 ; ugrás ha op1 > op2 A rendelkezésre álló alakok itt is rendundanciát tartalmaznak. Példa : cmp al,20 jg ide ; al-ben 30 van ; a program az ide cimkén folytatódik 5.23 Flagek értékétôl függô utasítások Ezek az utasítások a
következôk : 38 5.2 Feltételes ugró utasítások jc js jo jz jp jpe jpo cimke cimke cimke cimke cimke cimke cimke ; ugrás, ha CF = 1 ; ugrás, ha SF = 1 ; ugrás, ha OF = 1 ; ugrás, ha ZF = 1 ; ugrás, ha PF = 1 ; ugrás, ha PF = 1 ; ugrás, ha PF = 0 Ahogy az elôzôekben láttuk, mindegyik utasításnak érvényes a tagadása is : jnc jns cimke cimke ; ugrás, ha CF = 0 ; ugrás, ha SF = 0 jno jnz jnp jnpe jnpo cimke cimke cimke cimke cimke ; ugrás, ha OF = 0 ; ugrás, ha ZF = 0 ; ugrás, ha PF = 0 ; ugrás, ha PF = 0 ; ugrás, ha PF = 1 A redundanciára vontakozó megállapítás a PF-re nézve ugyancsak érvényes ! Példa : mov ah,1 int 21h cmp al,13 jz sor1 ; egy karaktert olvasunk az al regiszterbe ; ENTER volt ? ; ha igen akkor a sor1 cimkén folytatódjon ; a program . sor1: ; itt jönnek a sor1 cimkéhez tartozó utasítá; sok A fejezet elején említettem, hogy elôfordulhat az az eset amikor a -127 ill. +128 byte távolságnál nagyobbat kell
ugranunk. Ebbôl probléma származik, ugyanis a feltételes ugó utasításokkal csak ez a távolság kezelhetô. Ez a probléma a jmp utasítás használatával oldható meg : Példa : cmp ax,40 ja cimke ; cimke: ; ax-ben 50 van ; a cimke-re kell ugratnunk az ugrás helye és célja közötti távolság 200 byte ; itt jönnek a cimke-hez tartozó uta; sítások 39 ! 5. Feltételnélküli és feltételes ugró utasítások Mivel 200 byte-ot nem tudunk a j a utasítással átugrani, ezért a következô megoldást alkalmazzuk : Példa : cmp ax,40 jbe tovabb jmp cimke tovabb: cimke: ; ax-ben 50 van ; ez nem teljesül ; ez hajtódik végre ; itt jönnek a tovabb-hoz tartozó uta; sítások ; itt jönnek a cimke-hez tartozó uta; sítások Nézzük meg a feltételes ugróutasítások használatát egy rövid programon ! Beolvasunk két karaktert, összehasonlítjuk ôket, majd kiirjuk az összehasonlítás eredményét : 3. sz példaprogram : .MODEL small .STACK 100h
.DATA c1 db ? c2 db ? nagy12 db 1 > 2 ,$ kis12 db 1 < 2 ,$ egy12 db 1 = 2 ,$ sorem db 0dh,0ah,$ .CODE mov ax,@data mov ds,ax mov ah,1 int 21h mov [c1],al ; Elsô karakter C1-ben int 21h mov [c2],al ; Második karakter C2-ben ; cmp [c1],al ; Elsô összehasonlítva másodikkal je egyen ; Ha egyenlôk, akkor ugrás EGYEN-re jb k1 2 ; Ha elsô < második, akkor ugrás ; K1 2-re mov dx,OFFSET nagy12 jmp kiir egyen: mov dx,OFFSET egy12 40 ; Egyébként elsô > második Ellenôrzô kérdések és feladatok jmp kiir k1 2: mov dx,OFFSET kis12 kiir: mov ah,9 ; DOS print string funkcio int 21h mov ah,2 ; Egyik karakter kiiratása mov dl,[c1] int 21h mov dl,20h ; Szóköz kiiratása int 21h mov dl,[c2] ; Másik karakter kiiratása int 21h mov ah,9 ; Soremelés kiiratása mov dx,OFFSET sorem int 21h mov ah,1 int 21h ; Várakozás egy billentyűre mov ah,4ch ; DOS programbefejezes funkcio int 21h END Ellenôr zô kér dések és feladatok 01. Mire szolgál a jmp utasítás,
milyen működési módjai vannak ? 02. Mire szolgálnak a feltételes ugró utasítások, mely három típusba sorolhatóak ? 03. Hogyan tudjuk megvalósítani a -127 ill. +128 byte intervallumon kivülesô távolságú ugrásokat ? 04. Irjon programot amely annyival bôvíti a 3. sz példaprogramot, hogy a két beolvasott karaktert a nagyságuk szerinti sorrendben írja vissza a képernyôre. Például : beolvasva : b,a - kiírva : a,b 05. Irjon programot amely három karaktert olvas be. Elsô lépésben döntse el, hogy a három karakter közül melyik a legnagyobb. Második lépésben rakja ABC sorrendbe a három karaktert, és így irassa ki ôket a képernyôre ! 41 5. Feltételnélküli és feltételes ugró utasítások 06. Tegye intelligensebbé a 05. sz feladatban szereplô programot A kiiratás után kérdezze meg a program, hogy akarunk-e még karakter hármasokat beolvasni. Ha a válasz igenlô ( ‘ i’ vagy ‘ I’ ), akkor adja vissza a vezérlést a
beolvasásra. Ezt egész addig ismételje, amíg a kérdésre az ‘ n’ vagy ‘ N’ billentyűk valamelyikét nyomjuk le ! 07. Irjon programot, amely beolvas egy számjegynek megfelelô karaktert, és átalakítja számmá ! Például az ‘ 1’ -bôl 1-et csinál ! ( Az ‘ 1’ ASCII kódja 49 ). 08. Irjon programot, amely beolvas egy kétjegyű számnak megfelelô két karaktert, és átalakítja számmá ! Például az ‘ 1’ ‘ 2’ -bôl 12-ôt csinál ! 09. Irjon programot, amely beolvas három darab kétjegyű számnak megfelelô karaktersorozatot. A karaktersorozatot alakítsa át kétjegyű számokká, és döntse el hogy a számok alapján szerkeszthetô-e háromszög. 10. Tegye intelligensebbé a 09. sz feladatban szereplô programot ! A program jelezze, ha nem számjegynek megfelelô karaktert billentyűztünk és kérje be újra a karaktert ! 11. Irjon programot amely egy négyjegyű szám számjegyeit olvassa be, majd elvégzi a számmá alakítást !
Ezután bontsa le megint számjegyekre, és fordított sorrendben írja ki a számjegyeket a képernyôre. 42 6. Ciklusszer vezô utasítások Σ A feltételnélküli és feltételes ugró utasítások ismertetése után a ciklusszervezô utasításokkal ismerkedünk meg. A ciklusszervezô utasítások a harmadik alapvetô programstruktúra - az iteráció - megvalósítására szolgálnak. Segítségükkel tehát egy programrészlet végrehajtását többször is megismételhetjük. Ebben a fejezetben mutatom be a Turbo Debugger ( TDEXE ) használatát is. 6.1 A loop utasítás Szintaxis : LOOP <cimke> Jelenlegi tudásunk alapján a következôképpen szervezhetnénk ciklust : Példa : mov cx,10 cikl: . . . dec cx jnz cikl ; itt jönnek a ciklusmag utasításai A loop utasítás a dec cx, j nz cikl utasításokat valósitja meg ebben az esetben : Példa : mov cx,10 cikl: . . . loop cikl A loop utasításról elmondhatjuk tehát, hogy a cx tartalmát eggyel
csökkenti, majd - ha cx tartalma nem egyenlô nullával - az utána írt címre adja a vezérlést. Amennyiben cx tartalma nullával egyenlô, úgy a loop utasítást követô utasításon folytatódik a program. ! A loop utasítás csakis és kizárólag a cx regiszterrel működik együtt ! A loop utasítás egy elôjeles byte-on ábrázolja az ugrás hosszúságát, tehát -127 bytenál többet nem lehet visszafelé ugratni ! Ennél hosszabb ciklusok tehát nem 6.2 A loope és a loopne utasítások szervezhetôk a loop utasítással. A loop utasítás abban a tekintetben nem egyezik meg a dec cx, j nz cimke utasításpárral, hogy egyetlen flaget sem befolyásol, míg a dec utasítás igen ! Látható, hogy a loop utasítással csökkenô ciklusváltozójú ciklust tudunk csak megvalósítani. Növekményes ciklus megvalósítását kizárólag hagyományos eszközökkel érhetjük el, külön utasítás nem áll rendelkezésre ! Példa : mov dx,0 eleje: . . . inc dx cmp
dx,10 jnz eleje ; A ciklus utasításai ; Dx =10 - re a ciklus befejezi működését A loop utasításnak igen hasznos módosulatai állnak rendelkezésre, amelyek feltételes ciklusszervezést tesznek lehetôvé. 6.2 A loope és a loopne utasítások Szintaxis : LOOPE <cimke> , LOOPNE <cimke> Mindkét utasítás a cx regiszterrel működik együtt. A loope addig működteti a ciklust amíg cx tartalma pozitív és a ZF = 1. A loopne addig működteti a ciklust amíg cx tartalma pozitív és a ZF = 0. A loop utasításnál ismertetett megkötések itt is érvényesek ! A loope helyett loopz a loopne helyett loopnz is írható ! Példa : ; Beolvasó ciklus 20 karakterre, vagy az ENTER billentyű ; megnyomására beo cikl: mov cx,20 mov ah,1 ; int 21h mov [kar],al cmp al,13 loopne beo cikl . ; karakter az al-ben ; karakter a kar nevű változóban ; összehasonlítás az ENTER kódjával ; ha cx tartalma pozitív és nem e; gyenlô 13-mal, ( cx > 0 és ZF =
0 ) ; akkor folytatás a beo cikl címkén. ; a program folytatása 43 6. Ciklusszervezô utasítások Létezik még egy feltételes ugró utasítás aminek az ismertetése mégis ide kivánkozik. Ez a j cxz utasítás Szintaktikája a következô : jcxz <cimke> Abban az esetben ha a cikluba való belépés elôtt cx tartalma nulla és ezt az utasítást leírjuk, akkor a cimkére adódik a vezérlés. A j cxz segítségével tehát átugorhatunk egy olyan ciklust, amelynek ciklusváltozója - azaz a cx regiszter - nulla. Esetleg felmerülhet a kérdés, hogy miért probléma ha a cx tartalma nulla ? Magasszintű programnyelvek esetén ilyenkor a for ciklus egyszer sem fut le. Mint oly sok mindenben, az assembly ebben is eltér a magasszintű nyelvektôl. Ha a cx regiszter tartalma nulla, abban az esetben a ciklus egyszer mindenképpen lefut, hiszen a loop utasítás csak a ciklus végén teszteli a cx regisztert. A tesztelés elôtt azonban a loop levon egyet a cx
tartalmából, tehát annak értéke -1 lesz. A -1 hexadecimálisan ábrázolva : 0FFFFh, vagyis mind a 16 bit értéke 1. A ciklus tehát ahelyett, hogy egyszer sem fut le, 65535-ször futna le, ha nem védenénk ki ezt az esetet a j cxz használatával ! 6.3 A Tur bo Debugger használata A Turbo Debugger használatát egy példaprogram segítségével mutatom be. Elöljárójában annyit, hogyha használni akarjuk a debuggert, akkor ahhoz a következô kapcsolók szükségesek a fordításnál és a linkelésnél : Legyen a programunk neve proba.asm ! Fordítás : Linkelés : Debugger : tasm /zi tlink /v td proba[.asm] proba[.obj] proba[.exe] A [] zárójelpárban szereplô karakterek opcionálisak. A debugger (“ rovartalanító” ) arra alkalmas, hogy akár lépésenként is tudjuk futtatni a programunkat ha ez szükséges, illetve regiszterek és/vagy memóriaváltozók értékeinek alakulását kisérjük figyelemmel. Ezenkívül sok lehetôség áll rendelkezésre.
Megfigyelhetjük a stack illetve a flagek állapotát külön külön illetve együtt a CPU panelen is A debugger legfelsô sorában egy legördülô menüsor található, ennek segítségével választhatunk a különféle opciók között. Most nézzünk meg pár funkciót részletesen a következô példaprogram segítségével : Bekérünk egy számjegynek megfelelô karaktert, majd számmá alakítjuk ( levonunk belôle 48-at, azaz a ‘ 0’ ASCII kódját ). 44 6.3 A Turbo Debugger használata Ezután a számot egy integer alsó byte-jának tekintve, megfordítjuk az integer alsó és felsô byte-ját, például az 1-bôl 256 lesz. 4. sz példaprogram : .MODEL small .STACK 100h .DATA szam DW 0 .CODE mov ax,@data mov ds,ax ; DS regiszter inicializálása ; ; xor ax,ax ; AX regiszter nullázása mov ah,1 int 21h ; Egy karakter bekérése ; xor ah,ah ; AH regiszter nullázása sub al,48 ; Numerikus értékké alakítás ; xchg ah,al ; Az integer (AX) megfordítása ; mov
[szam],ax mov ah,4ch int 21h ; Visszatérés a DOS-hoz END Tételezzük fel, hogy programunk neve int swap.asm Ebben az esetben a következô lépéseket kell végrehajtani : For dítás L inkelés Debugger : : : tasm /zi int swap tlink /v int swap td int swap Természetesen az int swap kiterjesztése lépésenként rendre : .asm, obj, exe! Miután a td-t elindítottuk kék háttéren sárga betűkkel megjelenik a programunk forrásszövege. A képernyô legfelsô sorában egy menüsor látható, a kék hátterű program ablak alatt pedig egy szürke hátterű “ watch” ablak, ahol majd a kijelölt változoink értékét követhejük figyelemmel. A képernyô baloldalán, közvetlenül a .CODE direktíva után egy balramutató nyíl jelzi, hogy a debugger a programunk elejére állt. 45 6. Ciklusszervezô utasítások Mielôtt elindítanánk a programunkat, jelöljük ki megfigyelésre a szam változót illetve az ax, al, ah regisztereket. Ezt úgy tehetjük meg, hogy a
programunkban a kurzort - az egér, vagy a billentyűzet segítségével - ráállítjuk a kívánt változó vagy regiszter nevére és lenyomjuk a <CTRL> <W> billentyűkombinációt. Ezen művelet hatására a képernyô alján rendre megjelennek a kijelölt regiszterek illetve a szam változó nevei, valamint az éppen aktuális értékük. Ha most az <F8> billentyű segítségével lépésenként futtajuk a programunkat, akkor a következôket figyelhetjük meg : - Egy <F8> lenyomása egy sorral viszi lejjebb a balramutató nyilat. - Az int 21h utasításhoz érve a debugger átadja nekünk a vezérlést, abból a célból, hogy billentyűzhessünk egy karaktert. Ennek megtétele után a billentyűzött karakter ASCII kódja az al regiszterbe kerül. - Most látható, hogy miért kell 48-at kivonni az al regiszter tartalmából. Ha az 1-et billentyűztük, akkor az al-be 49 kerül, ha ebbôl 48-at kivonunk , akkor kapjuk meg az 1-et. - Az xchg ah,al
utasítás végrehajtása után látszik, hogy az integer vagyis az ax regiszter tartalma - valóban “ megfordult” , hiszen az ax regiszter tartalma 256 lett. - Utolsó elôtti lépésként kiírjuk ezt azt értéket a szam változóba, majd visszadjuk a vezérlést a DOS-nak. Amennyiben újra szeretnénk futtatni a programot, akkor a <CTRL > <F2> billentyű kombináció lenyomásával állíthatjuk az IP regisztert a programunk elejére. A szövegkurzor a <CTRL > <O> lenyomására áll a programszöveg elejére. ! Természetesen a szövegkurzor program elejére történô állítása nem helyettesíti az IP regiszter inicializálását ! Amennyiben egy lépésben szeretnénk lefuttatni a programot úgy az <F9> billentyűt nyomjuk le ! Lehetôség van arra is, hogy a programnak egy részét egy lépésben, míg a fennmaradó részt lépésenként futtassuk. Állítsuk a szövegkurzort a mov [szam],ax utasításra és nyomjuk le az <F4>
billentyűt. Ennek hatására egy lépésben történik meg a karakter bekérése, a 48 kivonása az al regiszterbôl valamint az ah és al megcserélése, majd a program végrehajtása megáll az általunk kijelölt utasításon és innét lépésenként hajthatjuk végre. Ez a 46 Ellenôrzô kérdések és feladatok lehetôség különösen akkor jó, ha a programunk egy részét már “ belôttük” , és nem akarjuk lépésenként futtatni. A Turbo Debugger ilyen szintű ismerete már elégséges ahhoz, hogy programjainkat javítani tudjuk. További ismeretek a debugger help menüjében állnak rendelkezésre. A fejezetet egy további példaprogrammal zárjuk. Egy olyan programot írunk, amely maximum 70 karaktert olvas be, de ha az <ENTER> billentyűt lenyomjuk, akkor vége van a beolvasásnak. Beolvasás közben meg kell számolni, hogy hány darab számjegyet billentyűztünk, és ezt a mennyiséget ki kell iratni az szj változóba ! 5. sz példaprogram : .MODEL
small .STACK 100h .DATA ; szj dw 0 ; .CODE mov ax,@data mov ds,ax ; mov cx,70 xor ax,ax mov dx,0 ; DX-ben számoljuk a számjegyeket mov ah,1 ; beolv: int 21h cmp al,0 jb nem ; Ha kisebb mint 0, akkor nem számjegy cmp al,9 ja nem ; Ha nagyobb mint 9, akkor sem számjegy ; inc dx ; Ha egyik sem tejesül akkor számjegy volt ! nem: cmp al,13 ; ENTER-re vizsgálunk loopne beolv ; mov [szj],dx ; mov ah,4ch int 21h END Ellenôr zô kér dések és feladatok 47 6. Ciklusszervezô utasítások 01. Mire használjuk a loop utasítást, melyik regiszterrel működik együtt ? 02. Meddig működik a ciklus loope és meddig loopne esetén ? 03. Hogyan írna meg egy 128 byte-nál hosszabb ciklust ? 04. Miért okoz problémát, ha a cx regiszter 0 kezdô értékével akarunk belépni egy loop ciklusba ? 05. Módosítsa úgy a 4. sz példaprogramot, hogy jelezze azt, ha nem számjegyet billentyűztünk ? 06. Irjon programot amely hasonló az 5. sz példaprogramhoz, csak nem a
számjegyek, hanem a magánhangzók számát állapítja meg ! Magánhangzók alatt csak az ‘ a’ , ‘ e’ , ‘ i’ , ‘ o’ , ‘ u’ értendô ! 07. Irjon programot amely nem az <ENTER>, hanem az <Esc> billentyűre fejezi be a karakterek beolvasását, és a karakterek száma nincs korlátozva. Meg kell állapítani, hogy hány sort és hány mondatot gépeltünk be. Mondatzáró karakterek : ‘ ’ , ‘ !’ , ‘ ?’ Az <Esc> billentyű ASCII kódja : 27 (1bh). 48 7. M emór ia címzési módok Σ Az elôzô fejezet alapján rendelkezésre állnak a vezérlésátadó uatsítások is. Az igazán hatékony programok elkészítését többek között azonban az gátolja, hogy csak egyszerű változókat tudunk kezelni az eddig tanultak alapján. A most következô fejezetben megismerkedünk az i8086-os processzor memória címzési módjaival, amelyek a következôk : közvetlen operandusú, direkt, indirekt, indexelt. Ezután képesek leszünk
összetet adatokkal ( vektorok, mátrixok ) dolgozni. 7.1 K özvetlen oper andusú címzés A közvetlen operandusú címzést már használtuk. A nevét onnan kapta, hogy a forrásoperandus kódja része lesz az utasítás gépi kódjának. Példa : mov ax,7 7.2 Dir ekt címzés A direkt címzést is használtuk már. Az elnevezés onnan ered, hogy a forrásoperandusra közvetlenül a nevével hivatkozunk Példa : mov ax,[adat] ; az adat egy dw típusú változó Látható, hogy ezzel a két címzési móddal nem tudnánk kezelni egy vektort, ugyanis egyik móddal sem tudunk indexelni. Az az eljárás pedig, hogy a vektor mindegyik elemét egyszerű változóként kezeljük, gyakorlatilag megvalósíthatatlan A következô két címzési mód teszi lehetôvé az összetett adatszerkezetek kezelését 7.3 I ndir ekt címzés Mint a neve is mutatja, az operandusra nem az értékével és nem is a nevével fogunk hivatkozni ! Ebben az esetben az operandusra a címével
hivatkozunk, 7.3 Indirekt címzés amelyet egy regiszterben fogunk eltárolni. Ez a regiszter aztán tetszés szerint növelhetô illetve csökkenthetô, tehát a következô vagy az elôzô elem címét tudjuk elôállítani, azaz képesek vagyunk indexelni ! Címregiszterként csak a bx, si, di regiszterek használhatók ! Példa : ; indirekt hivatkozás egyszerű változóra mov bx, OFFSET adat1 . . . mov ax,[bx] Példa : kiir: ! ; bx-ben lesz az adat1 címe ; utasítások ; ax-be kerül a bx által muta; tott címen lévô adat, tehát az ; adat1 változó tartalma. ; indirekt hivatkozás összetett változóra .DATA adat2 db ‘ 12345’ . . . .CODE . . . mov si,OFFSET adat2 . . . mov ah,2 mov cx,5 mov dl,[si] int 21h inc si loop kiir ; további adatdefiníciók ; utasítások ; utasítások ; kiiratjuk az adat2 karakter; vektor öt elemét. Abban az esetben, ha az indirekt hivatkozást tartalmazó kifejezés másik tagja regiszter, tehát egyértelműen kijelöli
a műveleti szélességet, akkor nincs semmi tennivalónk. Abban az esetben viszont, ha a másik operandus például egy konstans, akkor nekünk kell kijelölni a műveleti szélességet. Arról ugyanis nem készül nyilvántartás, hogy a címregiszterben lévô címen milyen típusú 49 ! 7. Memória címzési módok adat található. A műveleti szélességet a BYTE PTR és a WORD PTR operátorokkal jelölhetjük ki. Példa: mov [bx],1 mov BYTE PTR [bx],1 mov WORD PTR [bx],1 ; az 1 8 biten is kiirható, de 16 biten ; is. Nekünk kell tehát eldönteni, ; hogy hány bites legyen a műveleti ; szélesség ! ; kiirás 8 biten ; kiirás 16 biten 7.4 I ndexelt címzés Az indexelt címzésben két regiszter is részt vehet a cím kiszámításban, az egyiket bázis-, a másikat indexregiszternek hívjuk. Mátrixok kezelésére ez a címzés alkalmasabb mint az elôbbi. Az indexelt címzés általános képlete a következô : Telj es cím = [bázisr egiszter + indexr egiszter
+ eltolás ] Az egyes elnevezések a következôket jelentik és az alábbi értékeket vehetik fel : bázisregiszter : Ezzel a regiszterrel mondjuk meg, hogy az adat-, vagy a stack szegmenshez képest kívánunk e címezni. Amennyiben bx az értéke akkor elôbbi, amennyiben bp az értéke, akkor utóbbi. indexregiszter : A bázis regiszterhez viszonyított másik címösszetevôt tartalmazza. Értéke az si regiszter illetve a di regiszter lehet. eltolás : A teljes cím konstans része. Értéke egy memór iacím, vagy egy konstans lehet. A következôkben mindegyikre látunk majd példát ! Az eddigi ismereteinket felhasználva a képletünk a következôképpen alakul : BX 50 SI 7.4 Indexelt címzés Telj es cím = vagy BP + vagy + Eltolás DI A képletet kifejtve 16 konkrét memóriacímzési módot tudunk elôállítani : 01. 02. 03. 04. [eltolás] [bx] [si] [di] 09. 10. 11. 12. (direkt címzés) (indirekt címzés) (indirekt címzés) (indirekt
címzés) [bx + eltolás] [bp + eltolás] [si + eltolás] [di + eltolás] 05. 06. 07. 08. [bx + si] [bx + di] [bp + si] [bp + di] 13. 14. 15. 16. [bx + si + eltolás] [bx + di + eltolás] [bp + si + eltolás] [bp + di + eltolás] Abban az esetben, ha az eltolás memóriacím, a [bx + si + eltolás] alak a következôképpen is írható : eltolás[bx][si]. Az írásmód nem változtatja meg a címzés működését. A két regiszter tartalma összeadódik, ehhez hozzáadódik az eltolás és így jön létre a teljes cím ! Példa : mov ax,[matrix + bx +si] mov ax,matrix[bx][si] ; ax-be kerül a matrix aktuális ; eleme. ; ugyanazt a műveletet hajtja ; végre, mint az elôzô utasítás Az i8086-os assemblyben nem létezik pointeraritmetika, mint a C nyelvben. Itt nekünk kell arra ügyelnünk, hogy a címzésben szereplô regisztereket, helyesen inkrementáljuk vagy dekrementáljuk. Nyilvánvaló, hogy db változótípus esetén az indexregisztert elég eggyel növelni, hogy
a következô elem címét megkapjuk, de dw típusnál kettôt kell az index regiszterhez hozzáadni, mivel az integer változó hossza két byte ! A most következô példaprogramban beolvasunk egy maximum 40 hosszú kisbetűs karaktersorozatot, átalakítjuk nagybetűssé és kiiratjuk. 6. sz példaprogram : .MODEL small .STACK 100h .DATA 51 ! 7. Memória címzési módok kars db 40 dup(?) nagy db 41 dup(?) sorem db 0dh,0ah,$ ; .CODE mov ax,@data mov ds,ax ; mov si,0 mov cx,40 ; beo: mov ah,1 ; Itt kezdôdik a beolvasó ciklus int 21h mov kars[si],al inc si ; Csak eggyel kell növelni az ; indexregisztert cmp al,0dh loopne beo ; mov cx,si mov si,0 ; konv: nkis: ; Itt kezdôdik az átalakítás mov al,kars[si] cmp al,a jb n kis cmp al,z ja nkis sub al,20h ; A kisbetű és a nagybetű ASCII ; kódja között 20h a különbség mov nagy[si],al inc si loop konv mov nagy[si],$ ; A kiiratás miatt kell ! xor ax,ax mov ah,9 mov dx,OFFSET nagy int 21h mov dx,OFFSET sorem int 21h ;
mov ah,4ch int 21h END 52 7.4 Indexelt címzés A következô példaprogramban két 10 elemű integer vektort adunk össze. Figyeljük meg, hogy itt már nem elég az indexregiszter eggyel való növelése, hiszen az integer változó hossza 2 byte ! A példaprogram eredményét egyelôre a Turbo Debugger ( továbbiakban TD ) segítségével tudjuk megtekinteni. 7. sz példaprogram : .MODEL small .STACK 100h .DATA ; v1 dw 1,2,3,4,5,6,7,8,9,10 v2 dw 1,2,3,4,5,6,7,8,9,10 v3 dw 10 dup(0) ; .CODE mov ax,@data mov ds,ax ; mov si,0 mov cx,10 ; ad: mov ax,v1[si] add ax,v2[si] mov v3[si],ax inc si ; Az SI indexregisztert kettôvel inc si ; kell növelni ! loop ad ; mov ah,4ch int 21h END A következô példaprogramban egy C típusú ( bináris nulla lezárású ) string hosszát fogjuk megállapítani. A hossz változóban lévô értéket a TD segítségével nézzük meg ! 8 sz. példaprogram : 53 7. Memória címzési módok .MODEL small .STACK 100h .DATA ; szoveg db FRADI,0
hossz dw 0 ; .CODE mov ax,@data mov ds,ax ; mov bx,0 kezd: mov al,szoveg[bx] cmp al,0 jz bef inc bx jmp kezd bef: mov [hossz],bx ; mov ah,4ch int 21h END prg ; BX tartalmazza a hosszat Most egy stringet fogunk átmásolni egy másik stringbe. Az eredménystringet kiiratjuk. 9 sz. példaprogram : .MODEL small .STACK 100h .DATA innen db Lisztes berúgta elsô Bundesliga gólját !,0 ide db 80 dup(?) hossz dw 0 .CODE mov ax,@data mov ds,ax ; mov si,0 hossza: ; Másolandó string hosszának megállapítása mov al,innen[si] inc si ; SI-ben lesz a hossza cmp al,0 54 7.4 Indexelt címzés jnz hossza mov [hossz],si ; Látszólag felesleges ; de célszerű kimenteni ! mov cx,[hossz] mov bx,0 ; masol: mov al,innen[bx] mov ide[bx],al inc bx loop masol mov ide[bx],$ ; A kiiratás miatt kell a ‘ $’ xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; Itt iratjuk ki a másolatot ; mov ah,4ch int 21h END Egy 3x3-as karaktermátrix kiiratása következik. Ehhez a mátrixot indexelnünk kell
ami azt jelenti, hogy elô kell állítanunk tetszôleges elemének a címét. Ezt a műveletet a következô képlet segítségével tudjuk elvégezni : Elem címe = Mátrix kezdôcíme + Sorindex* Oszlopméret + Oszlopindex A képlet természetesen feltételezi a pointeraritmetikát. Mivel az i8086-nál ez nem áll rendelkezésre, ezért integer mátrix esetében a képlet 2. és 3 tagját nekünk kell megszorozni kettôvel ! 10 sz. példaprogram : .MODEL small .STACK 100h .DATA ; matrix DB 1,2,3 DB 4,5,6 DB 7,8,9 elemsz DW 3 sorem DB 0dh,0ah,$ ; 55 7. Memória címzési módok .CODE mov ax,@data mov ds,ax ; mov cx,0 ; Sorindex cikl1: mov di,0 ; Oszlopindex kiir : mov al,cl mul BYTE PTR [elemsz] ; Sorindex* Oszlopméret mov bx,ax mov si,di ; Oszlopindex mov ah,2 mov dl,matrix[bx][si] ; A mátrix eleme int 21h mov dl,20h ; Egy szóköz kiiratása int 21h inc di cmp di,[elemsz] jnz kiir ; mov ah,9 mov dx,offset sorem ; Soremelés kiiratása int 21h inc cx cmp cx,[elemsz] jnz
cikl1 ; mov ah,9 mov dx,OFFSET sorem int 21h mov ah,1 int 21h ; mov ah,4ch int 21h END ; Várunk egy billentyü lenyomására Ellenôr zô kér dések és feladatok 01. 56 Milyen memória címzési módokat ismer az i8086-os processzor ? Ellenôrzô kérdések és feladatok 02. Mi az indexelt címzés alapképlete, milyen regisztereket használhatunk és mi ezeknek a regisztereknek a szerepe ? 03. Mi célt szolgálnak a BYTE PTR és a WORD PTR operátorok ? 04. Hogyan tudjuk “ kivédeni” azt, hogy az i8086 assembly-ben nem létezik pointeraritmetika ? 05. Hogyan számítjuk ki egy mátrix tetszôleges elemének a címét ? 06. Irjon programot, amely beolvas egy maximum 40 hosszú karaktersorozatot és eltárolja egy vektorban. Másolja át egy másik vektorba, de fordított sorrendben. Irassa ki az eredményvektort ! 07. Irjon programot amely bekér két egyenlô hosszú stringet. ( Maximum 40 karakter hosszút ). Állapítsa meg, hogy melyik string a nagyobb
illetve egyenlôek-e ! A megállapítást irassa is ki a stringekkel együtt ! ( Az egyik string akkor nagyobb mint a másik, ha az adott pozición nagyobb ASCII kódú karakter áll mint a másikban ). 08. Bôvítse úgy a 07. számú programot, hogy alkalmas legyen két string viszonyának megállapítására akkor is, ha azok nem egyenlô hosszúak. 09. Irjon programot, amely összefűz két C típusú stringet olymódon, hogy az elsôt bemásolja egy célstringbe, majd utána fűzi a másodikat. Az eredmény irassa ki ! 10. Irjon programot, amely összefűz két C típusú stringet olymódon, hogy az elsô string egy karakterét bemásolja egy célstringbe, majd utána a második string egy karakterét. Elôszö a két string lehet egyenlô hosszú, majd próbálja meg különbözô hosszú stringekkel megoldani a feladatot! Az eredményt irassa ki ! 11. Vonjon ki egymásból két 10 elemű integer tömböt. Az eredményt a TD-vel tekintse meg ! 12. Képezze két 10
elemű integer vektor skaláris szorzatát. A szorzat eredményét a TD-vel tekintse meg ! 13. A 10. sz példaprogram alapján indexeljen egy 5x5-ös integer mátrixot Az aktuális elemet írja az ax regiszterbe és a TD-vel tekintse meg ! 57 8. L éptetô és flagekkel kapcsolatos utasítások A fejezetben szó lesz a balra illetve jobbra történô bitléptetésrôl, illetve rotációról egy adott byte-on, vagy szón belül. Megtárgyaljuk ezek különbözô válfajait, úgymint aritmetikai léptetés illetve rotáció a CF-en keresztül. A fejezetet a flagekkel kapcsolatos utasítások ismertetése zárja. ∑ 8.1 Az SHL és SAL utasítások Szintaxis : SHL <op1>,<op2> , SAL <op1>,<op2> Az shl utasítás annyival lépteti balra az <op1> bitjeit a CF-en keresztül, amennyivel az <op2>-ben meghatároztuk. A balra léptetett alacsony helyiértékű bitek helyébe nullát ír. Az <op1> lehet regiszter, vagy memóriaváltozó. Az
<op2> lehet egy konstans, vagy a cl regiszter Az sal (aritmetikai léptetés balra) utasítás működése teljesen megegyezik az shl utasítás működésével. Az érintett flagek a következôk : CF, PF, ZF, SF, OF Az i8086 assembly esetében a konstans csak 1 lehet. Amennyiben egyszerre több bittel akarunk balra léptetni, akkor ezt a számot a cl regiszterbe kell elôszôr írnunk és aztán alkalmazhatjuk az shl utasítást. Mind az shl, mind az sal utasítás 2 hatványaival való szorzást hajt végre. Példa : mov al,11001101b CF 0 Eredeti bitminta (al) 1 1 shl al,1 0 1 1 0 1 1 0 ; hatására így alakul az al tartalma CF 1 0 Balra léptetettt bitminta (al) 1 0 0 1 1 ; Egyszerre több bittel léptetünk balra 0 ! 8. L éptetô és flagekkel kapcsolatos utasítások mov al,11001100b mov cl,4 shl al,cl 8.2 Az SHR és SAR utasítások Szintaxis : SHR <op1>,<op2> , SAR <op1>,<op2> Az shr utasítás annyival lépteti
jobbra az <op1> bitjeit a CF-en keresztül, amennyivel az <op2>-ben meghatároztuk. A jobbra léptetett magas helyiértékű bitek helyébe nullát ír Az <op1> lehet regiszter, vagy memóriaváltozó Az <op2> lehet egy konstans, vagy a cl regiszter. Az sar ( aritmetikai léptetés jobbra ) utasítás működése nem egyezik meg az shr utasítás működésével. A különbség az, hogy az sar nem nullát ír a legmagasabb helyiértékre, hanem a léptetés utáni legmagasabb helyiértékű bit értékét ( 6. számú ), visszaírja a 7 számú bit pozíciójába. Ezzel gyakorlatilag az eredeti bináris szám elôjelét ôrzi meg az sar utasítás. Az érintett flagek a következôk : CF, PF, ZF, SF, OF. Az shl utasításnál tárgyalt <op2>-re vonatkozó megkötések itt is érvényesek. Az shr utasítás 2 hatványaival való elôjel nélküli, míg az sar utasítás 2 hatványaival való elôjeles osztást hajt végre. Példa : ! mov al,11001101b
Eredeti bitminta (al) 1 1 0 0 shr al,1 1 CF 1 0 1 0 ; hatására így alakul az al tartalma SHR-rel jobbra léptetett bitminta (al) 0 1 1 0 0 1 1 0 CF 1 ; ha az eredeti bitmintát az sar utasítással léptetjük: sar al,1 SAR-rel jobbra léptetett bitminta (al) 1 58 1 1 0 0 1 1 0 CF 1 8.3 A ROL és RCL utasítások ; Egyszerre több bittel léptetünk jobbra mov al,11001100b mov cl,4 shr al,cl 8.3 A ROL és RCL utasítások Szintaxis : ROL <op1>,<op2> , RCL <op1>,<op2> A r ol és r cl utasítások balra történô rotációt valósítanak meg. A rotáció hasonlít a léptetéshez ( shiftelés ), azzal a különbséggel, hogy a legalsó bit pozicióba nem nulla íródik, hanem a kiléptetett legfelsô bit inzertálódik be. Az <op1>-re illetve <op2>-re ugyanazok a megkötések igazak, mint a léptetô utasítások esetén. Az r cl utasítás a CF-en keresztül végzi a rotációt, azaz a kiléptetett legfelsô
bitet a CF-be írja, és a CF értékét inzertálja a legalsó bit pozicióba. Az utasítások a CF és az OF flageket érintik. Példa : mov al,11001101b CF 0 Eredeti bitminta (al) 1 1 rol al,1 0 0 1 1 0 1 ; hatására így alakul az al tartalma ROL-lal balra léptetettt bitminta (al) 1 0 0 1 1 0 1 1 ; ha az eredeti bitmintát az rcl-lel rotáljuk rcl al,1 CF RCL-lal balra léptetettt bitminta (al) 59 8. L éptetô és flagekkel kapcsolatos utasítások 1 1 0 0 1 1 0 1 0 ; Egyszerre több bittel rotálunk balra mov al,11001100b mov cl,4 ; shl al,cl 8.4 A ROR és RCR utasítások Szintaxis : ROR <op1>,<op2> , RCR <op1>,<op2> A r or és r cr utasítások jobbra történô rotációt valósítanak meg. A jobbra történô rotáció úgy valósul meg, hogy a legfelsô bit pozicióba a kiléptetett legalsó bit inzertálódik be. Az <op1>-re illetve <op2>-re ugyanazok a megkötések igazak, mint a léptetô
utasítások esetén. Az r cr utasítás a CF-en keresztül végzi a rotációt, azaz a kiléptetett legalsó bitet a CF-be írja, és a CF értékét inzertálja a legfelsô bit pozicióba. Az utasítások szintén a CF és az OF flageket érintik. Példa : mov al,11001101b Eredeti bitminta (al) 1 1 0 0 ror al,1 1 CF 1 0 1 ; hatására így alakul az al tartalma ROR-ral jobbra léptetett bitminta (al) 1 1 1 0 0 1 1 ; ha az eredeti bitmintát az rcr-lel rotáljuk: rcr al,1 60 0 0 8.5 Flagekkel kapcsolatos utasítások RCR-rel jobbra léptetett bitminta (al) 0 1 1 0 0 1 1 0 CF 1 ; Egyszerre több bittel rotálunk balra mov al,11001100b mov cl,4 ; shl al,cl 8.5 Flagekkel kapcsolatos utasítások Mint a nevükbôl is kiderül, ezek az utasítások bizonyos flagek értékének megváltoztatására szolgálnak. Ezek a flagek konkrétan : CF, DF, IF Az utasítások és hatásaik a következôk : CLC CMC STC CLD STD CLI STI CF = 0 CF = NOT CF CF = 1
DF = 0 DF = 1 IF = 0 IF = 1 A cld és std utasításokat az i8086-os string műveleteinél fogjuk használni, a cli és sti utasítások pedig interrupt kezelésnél használatosak. A most következô példaprogramban két 8 bites kódszó Hamming távolságát számítjuk ki. Hamming távolság alatt értjük azon bitpoziciók számát, amelyeken a kódszavak bitértékei különböznek. 11. sz példaprogram : .MODEL small .STACK 100h .DATA kod1 db 00001101b kod2 db 00101000b ht dw 0 61 8. L éptetô és flagekkel kapcsolatos utasítások .CODE mov ax, @data mov ds,ax ; xor ax,ax mov al,[kod1] ; xor al,[kod2] ; Megállapítjuk, hogy hol különböz; nek a bitértékek ! clc ; Megszámoljuk, hány db 1-es bitünk mov cx,8 ; leptet: shl al,1 jnc nem1 inc [ht] nem1: loop leptet mov ah,4ch int 21h END A másik példaprogramban szimuláljuk a soros protokolt. Bekérünk egy karaktert és kiiratjuk a bitjeit. Az adatátvitel egy start bittel kezdôdik, melynek értéke 0. Ezután
következik a 8 adatbit, majd a páros paritásbit Az adatátvitelt egy 1-es értékű stopbit zárja. 12. sz példaprogram : .MODEL small .STACK 100h .DATA be db Kérem a karaktert : ,$ kar db ? prot db A soros protokoll : ,$ sorem db 0dh,0ah,$ .CODE mov ax,@data mov ds,ax ; mov ah,9 mov dx,OFFSET be int 21h mov ah,1 ; A karakter beolvasása int 21h mov [kar],al ; 62 8.5 Flagekkel kapcsolatos utasítások mov ah,9 mov dx,OFFSET sorem int 21h mov ah,9 mov dx,OFFSET prot int 21h ; mov ah,2 ; START bit kiiratása mov dl,0 int 21h ; mov cx,8 mov bl,0 adatbitek: shl [kar],1 jc egyes ; Ha 1. volt a bit mov ah,2 mov dl,0 int 21h jmp cv egyes: mov ah,2 mov dl,1 int 21h inc bl cv: loop adatbitek ; ; test bl,1 ; PARITAS bit kiiratása jnz ptl ; Ha a legalsó bit nem 0 akkor ; páratlan mov ah,2 mov dl,0 int 21h jmp stopbit ptl: mov ah,2 mov dl,1 int 21h ; stopbit: ; STOP bit kiiratása mov ah,2 mov dl,1 int 21h ; mov ah,9 mov dx,OFFSET sorem int 21h ; mov ah,4ch int 21h 63 8. L
éptetô és flagekkel kapcsolatos utasítások END Ellenôr zô kér dések és feladatok 01. Milyen megkötések érvényesek az shl illetve sal utasítások két operandusára ? 02. Mi a különbség az shr és az sar utasítások működése között ? 03. Mi a különbség a léptetés és a rotáció között ? 04. Mi a különbség a rol és rcl valamint a ror és rcr utasítások között ? 05. Szorozza meg +8-cal a 00010100b bitmintát. Elôször lépésenként hatjsa végre a műveletet ( egyesével léptetve ) ! 06. Ossza el a -16-ot +8-cal ! A -16 binárisan : 11110000b. 07. Ossza el +8-cal lépésenként a következô bitmintát : 01110000b ! 08. Irjon programot amely a következô bitminta 5. bitjét komplementálja, de nem használhatja az xor utasítást. Az 5 bitet be kell “ csempészni” a CF-be, ott komplementálni kell, majd vissza kell vinni a helyére ! A bitminta a következô : 10100110b. A bitek sorszámozása 0-tól indul ! 64 9. Szubr
utinok szer vezése Σ Ebben a fejezetben megismerkedünk a szubrutinok szervezésével és használatával. A szubrutinok önálló alprogramok, amelyekre meghatározott hívási mechanizmus segítségével adhatjuk rá a vezérlést. A hívó programba való visszatérés is meghatározott mechanizmus szerint történik. Rokon fogalom a C-ben a függvény fogalma. 9.1 A szubr utin for mai szer vezése és hívása Szubrutint a következô formában tudunk megvalósítani : sub név sub név PROC . . . utasítások . . . ret ENDP A szubrutin - fôprogramhoz képesti - elhelyezése szokás kérdése, van aki a fôprogram elé, van aki mögé írja a szubrutinjait. Ebben a jegyzetben a szubrutinok a fôprogram mögött helyezkednek el. Az END direktíva most is a program legutolsó sora lesz ! Az általános definíció szerint a sub név a szubrutin nevét jelenti, melyet a PROC direktíva követ. Ezután a szubrutin “ testét” képezô utasítások következnek, majd a r
et utasítás. Ezen utasítás hatására tér vissza a szubrutin az ôt hívó programba. A szubrutint a sub név megismételt leírása, valamint az ENDP direktíva zárja. Mivel mi a small modellt használjuk ezért a szubrutinhívás mindig közeli lesz, vagyis a hívás során nem lépünk ki a 64 K-s kód szegmensbôl. Ezt a tényt az assembler a MODEL direktíva után írt small kulcsszó alapján regisztrálja. 9.2 A szubrutinhívás és visszatérés működése Van arra mód, hogy elôírjuk a hívás típusát : a PROC direktíva után a NEAR vagy a FAR direktívával megszabhatjuk ezt. Ennek megfelelôen a r et helyett a r etn vagy a r etf utasítást kell elhelyezni a szubrutin végén. A hívóprogramban a következô módon hívhatjuk meg a szubrutinunkat : call sub név A call utasítás hatására a vézérlés átadódik a sub név nevű szubrutinra, majd a szubrutin végén található r et utasítás hatására a vezérlés visszaadódik a hívóprogramra. A
hívóprogram a call utasítást követô utasításon folytatódik A következô fejezet részletezi a szubrutinhívás működését. 9.2 A szubr utinhívás és visszatér és működése A formai áttekintés után következzenek a lényegi dolgok. Amikor egy call utasításhoz érkezik a programunk, akkor nyilván meg kell szakítani a szekvenciális végrehajtást és egy adott nevű szubrutinra kell átadni a vezérlést. Az assembler által készített ún. szimbólumtábla segítségével a szubrutin nevéhez egy fizikai cím rendelôdik, amely cím a call utasítás végrehajtásakor beíródik az IP regiszterbe. Ezzel megtörtént ugyan a vezérlésátadás, de a probléma ott van, hogy a r et utasítás “ nem tudja” , hogy hova kell visszaadni a vezérlést. Ezért még mielôtt a szimbólumtáblában lévô fizikai címre átadnánk a vezérlést, el kell menteni a call utasítást követô utasítás címét annak érdekében, hogy a szubrutinbeli r et utasítás
vissza tudja adni a vezérlést a hívó programnak. Most már csak az a kérdés, hogy hova mentsük ezt az ún visszatérési címet ? Kézenfekvô a stack-be történô mentés. Ekkor ugyanis a r et utasítás hatására egyszerűen csak beíródik a stack tetejérôl a visszatérési cím az IP regiszterbe, és máris megtörtént a visszatérés. Öszefoglalva megállapíthatjuk tehát, hogy szubrutinhívásnál két lényeges dolog történik. Elôször a call utasítást követô utasítás címe - visszatérési cím beíródik a stackbe Ezután a call utasítást követô szimbólumhoz tartozó fizikai cím beíródik az IP regiszterbe. Ezzel megtörtént a vezérlésátadás a szubrutinra A vezérlés visszadása a hívóprogramra pedig úgy történik, hogy a r et utasítás hatására a stack tetején tárolt visszatérési cím beíródik az IP regiszterbe. Ez a mechanizmus természetesen feltételezi azt, hogy a szubrutin ne rontsa el a stack-et, tehát az esetleges stack
használat után állítsa vissza a stack eredeti állapotát. 65 ? 9. Szubrutinok szervezése Most láthatjuk, hogy mennyire kézenfekvô a stack használata szubrutin híváskor. Abban az esetben ugyanis, amikor egy szubrutinból meghívunk egy másik szubrutint - tehát két visszatérési címet kell tárolnunk - másféle adatszerkezet nem is jöhetne szóba a visszatérési címek kezelésére. A másodjára meghívott szubrutinban a r et utasítás hatására ugyanis azt a visszatérési címet kell beolvasni az IP-be, amelyet késôbb mentettünk el. 9.3 Par améter át - és visszaadás kér dése ? Elvileg természetesen léteznek olyan szubrutinok, amelyeknek sem bemenô paraméterük nincs és értéket sem adnak vissza. Általában azonban azért alkalmazunk szubrutint, hogy bizonyos adatokon bizonyos művelete(ke)t hajtsunk végre. Gyakran elôfordul az az eset is, hogy a szubrutin valamilyen értéket ad vissza, valamely művelet eredményeként. Mindenképpen
jogos tehát a kérdés: Milyen módon adhatunk át paramétereket egy szubrutinnak ? A paraméterátadás öt módját különböztetem meg és a késôbbiekben mindegyikre látunk példát. 1. 2. 3. 4. 5. Paraméter értékének átadása regiszteren keresztül. Paraméter címének átadása regiszteren keresztül. Paraméter értékének átadása stacken keresztül. Paraméter címének átadása stacken keresztül. Globális változó használata. A paraméter ér tékének átadása regiszteren keresztül talán nem igényel különösebb magyarázatot, az aktuális paramétereket a megfelelô regiszterekbe írjuk majd meghívjuk a kívánt szubrutint. Példa : 66 mov ax,[adat1] mov bx,[adat2] call szubrut1 ; A szubrut1 nevű szubrutin az AX-ben ta; lálja az elsô, míg BX-ben a második para; méterét. 9.3 Paraméter át - és visszaadás kérdése A paraméter címének regiszteren keresztül történô átadására akkor van szükség, amikor a paraméter
értékét a szubrutinban felül akarjuk írni. Ehhez amint azt a C nyelvben is láttuk - a paraméter címének átadására van szükség Példa : mov ax,OFFSET adat1 mov bx,OFFSET adat2 ; call szubrut2 ; A szubrut2 nevű szubrutin az AX-ben ta; lálja az elsô, míg BX-ben a második para; méterének a címét Joggal kérdezhetné valaki, hogy ha regisztereken keresztül is tudunk “ közlekedni” egy szubrutinnal, akkor miért kell a stacket használni paraméterátadásra ? A válasz egyszerű : 1. Egy nagyobb és fôleg bonyolultabb programnál nem pazarolhatunk regisztereket pusztán paraméterátadásra. Ilyenkor egyértelműen a stacken keresztül történik a paraméterátadás ! 2. Mint azt a késôbbiekben látni fogjuk, nem mindig választhatunk regiszteren keresztüli vagy stacken keresztüli paraméterátadás között ! A C nyelv és az assembly nyelv közötti kapcsolat során a paraméterátadás a stacken keresztül valósul meg - függvényhívás esetén.
Ahhoz, hogy a stackbe írni illetve onnan olvasni tudjunk, két utasítást kell megismernünk, de elôbb nézzük meg a globális változó használatát. A C nyelven globális változó alatt a main() elôtt definiált változót értjük, ami az egész forrásprogramra nézve látható. A mi esetünkben az adatszegmensben definiált változó látható az egész programra nézve, tehát minden további nélkül használhatjuk egy szubrutinban. Természetesen ennek a megoldásnak az alkalmazása csak a legvégsô esetben ajánlható ! 9.31 A PUSH és a POP utasítások Szintaxis : PUSH <op> , POP <op> A push utasítás a stackbe ír 2 byte-ot és az SP regiszter értékét kettôvel csökkenti, a pop utasítás pedig kettôvel növeli az SP regiszter tartalmát és a stackbôl kiolvas 2 byte-ot. Az <op> lehet 16 bites regiszter, vagy 16 bites 67 ! 9. Szubrutinok szervezése memóriaváltozó. Az utasítások a flageket nem érintik A következô kis
példaprogramot érdemes TD alatt lefuttatni úgy, hogy közben a VIEW menübôl a CPU panelt választjuk, és ott a jobb alsó sarokban figyeljük a stack helyzetének alakulását. 13. sz példaprogram : .MODEL small .STACK 100h .DATA adat dw 0 ; .CODE mov ax, @data mov ds,ax ; mov ax,1 mov bx,2 mov [adat],3 ; push ax push bx push [adat] ; mov ax,0 mov bx,0 mov adat],0 ; pop [adat] pop bx pop ax ; mov ah,4ch int 21h END ; Kinullázzuk ôket, hogy ellenôrizni ; tudjuk a kiolvasás helyességét. Ezekután nézzünk meg egy példát stacken keresztüli paraméterátadásra : A fôpr ogr am : . . . push ax call szubrut5 68 ; Az ax-ben lévô paraméter a stackbe kerül ; Meghívjuk a szubrutint 9.3 Paraméter át - és visszaadás kérdése add sp,2 . . . ; Helyreállítjuk a stacket, errôl majd késôbb ! A stack a következôképpen néz ki a szubr utin hívás után : OFFSET R. A AX 96 98 100 Az R. A rövidítés a Return Adress ( Visszatérési Cím ) kifejezésbôl
ered Mint tudjuk a szubrutinhívásnál ez a cím mindenképpen a stackben tárolódik. Az SP regiszter értéke 96, a stack tetejére mutat. Az egyszerűbb kezelhetôség érdekében decimális számokat alkalmazunk. Megtörtént a vezérlésátadás a szubr ut5 szubrutinra amely - mivel a stackbôl fogja “ kihalászni” a paraméterét - használni fogja a BP regisztert. Ezért elôbb a BP regisztert el kell menteni a stackbe. Ezáltal az SP regiszter értéke 94 lesz Az eredeti paraméter tehát relatíve 4 egységgel nagyobb címen helyezkedik el, mint az SP aktuális értéke. Ezt a tényt használja ki a szubrutin A szubr utin : szubrut5 PROC push bp mov bp,sp . . . mov bx,[bp+4] . . . pop bp ret ; BP-t a stackbe mentjük ; Most BP értéke is 94 ; Indexelt címzéssel érjük el a paraméterün; ket, amely most a BX-be került. ; Hatására a stack teteje a BP regiszterbe író; dik és az SP értéke 96 lesz. ; Hatására a stack tetejérôl az R. A az IP ;regiszterbe
íródik, és megtörténik a 69 9. Szubrutinok szervezése ;vezérlés visszaadása a hívóprogramra. Az ;SP értéke 98 lesz. szubrut5 ENDP Látható, hogy a hívóprogramba úgy térünk vissza, hogy az SP regiszter tartalma nem felel meg annak a tartalomnak amellyel meghívtuk a szubrut5 szubrutint. Ezért a stacket úgymond helyre kell állítani Ezzel kapcsolatban kétféle konvenció alakult ki : - A Pascal konvenció szerint a hívott pr ogr am állítja vissza a stacket. Jelen esetben a ret 2 utasítással tehettük volna ezt meg - A C konvenció szerint a hívott pr ogr am állítja helyre a stacket. Ezért található a fôprogramban a call szubrut5 utasítás után az add sp,2 utasítás ! Roppant fontos kérdés a - szubrutin által lerombolt - regiszterek mentése. Itt szintén két szokás alakult ki. Az egyik szerint a hívó program elôzetesen elmenti a kérdéses regisztereket és hívás után visszamenti ôket. A másik szerint a szubrutin menti el és
vissza a regisztereket Akármelyiket is használjuk, gondosan dokumentáljuk a szubrutinunkat. Amellett, hogy feltüntetjük a paramétereit, funkcióját és lehetséges visszaadott értékét, feltét-len tüntessük fel az általa lerombolt regisztereket is ! ! A szubrutin által visszadott érték háromféleképpen kerülhet vissza a hívó programba : 1. 2. 3. Visszaadott érték regiszterben. C nyelv és assembly kapcsolat esetén ez a regiszter egyértelműen az AX regiszter ! Visszadott érték memória változóban. Ez rendszerint úgy működik, hogy a memóriaváltozó címét kapja meg bemenô paraméterül a szubrutin, és erre a címre írja az értéket Globális változón keresztül. A most következô példaprogramban egy olyan hasznos szubrutinnal ismerkedünk meg, amely egy integert stringgé konvertál, majd kiirja a képernyôre. A konvertálás olyan elven történik, hogy az integer tízzel való osztásának maradéka adja a legalacsonyabb helyiértékű
számjegyet. Amennyiben ehhez 48-at adok, akkor máris karakterré konvertáltam. Ehhez az eljáráshoz, a konvertálási célterület végének a címét kell átadni, hiszen ide kerül a legalacsonyabb helyiértékű számjegy. Ezért jelöljük meg egy LABEL BYTE címkével a célterület vége utáni byte-ot, hogy ebbôl a címbôl egyet levonva átadhassuk a terület végének a címét. A szubrutint neve cnts Három paraméterét regiszterekben kapja meg : magát az integert az AX, az elôbb 70 9.3 Paraméter át - és visszaadás kérdése említett címet a BX, a kiirási szélességet a CX regiszter tartalmazza. A kiiró szubrutin neve pedig prst. Paraméterét a BX regiszterben kapja meg : ez a kiirandó string kezdôcíme. 14. sz példaprogram : .MODEL small .STACK 100h .DATA szam DW 334 ide DB 5 DUP (20h) vege LABEL BYTE DB 0dh,0ah,$ ; .CODE mov ax,@data mov ds,ax ; mov ax,[szam] vege - 1 ; keresztül mov cx,5 call cnts ; mov bx, OFFSET ide call prst ; ;
Paraméterátadás regiszteren mov bx,OFFSET mov ah,4ch int 21h ; Itt jon a CNTS fuggveny ; ; AX : Konvertálandó szám ; DS:BX : A string végére mutató pointer, ahol ; elhelyezzük a számot ; CX : A konvertálás szélessége ; cnts PROC mov si,10 ; 10 az osztó cnvlp: sub dx,dx div si add dl,0 mov [bx],dl dec bx ; Maradék a DX-ben ; Most konvertáltuk karakterré ; Kiirjuk az aktuális címre 71 9. Szubrutinok szervezése and ax,ax jz szokoz loop cnvlp ; Ha 0 az AX tartalma, akkor vége szokoz: ret cnts ENDP ; Itt jön a PRST függvény ; ; DS:BX : A stringre mutató pointer ; prst PROC push ax push dx ; mov ah,9 mov dx,bx int 21h ; pop dx pop ax ret prst ENDP END ? Gyakorlottabb programozókban lehet, hogy már felmerült a kérdés : ezentúl minden elkészült szubrutint forrásszinten tartalmaznia kell az aktuális programnak ? A választ a következôkben adom meg . 9.4 K ész szubr utinok szer kesztése, saj át könyvtár ak létr ehozása 72
9.4 Kész szubrutinok szerkesztése, saját könyvtárak létrehozása Természetesen nem szükséges minden szubrutint forrásszinten beinzertálni a fôprogramba. Az egyszer elkészített és “ belôtt” szubrutint vagy szubrutinokat lefordítva objekt modullá, hozzá linkelhetjük a fôprogramunk objekt modul-jához. Ehhez azonban a következôket kell megtennünk : 1. A fôprogramban a hivatkozott szubrutint elôször az EXTRN és a PROC direktívák segítségével, externként kell deklarálni. Ez mondja meg a fôprogramnak, hogy itt hívjuk meg a szubrutint, de annak megvalósítása nem itt található. Ez az eljárás a C nyelvben megfelel az EXTERN kulcsszó használatának. 2. A szubrutint tartalmazó modulban a szubrutint a PUBLIC direktíva segítségével kell deklarálni jelezvén a linkernek, hogy ezt a modult más modulok is használhatják, elérhetik. Példa : Fôprogramot tartalmazó modul .MODEL small .STACK 100h .DATA ; Adatdefiníciók .CODE EXTRN
cnts:PROC kezd: ; ; Utasítások ; END kezd Szubrutint tartalmazó modul cnts cnts PUBLIC cnts PROC ; ; Utasítások ; ENDP Legyen ezután a fôprogram neve fo prog.asm, a szubrutint tartalmazó modulé al prog.asm A fordítás illetve linkelés menete a következô : 1. tasm fo prg[. asm] 73 9. Szubrutinok szervezése Hatására létrejön a fo prg.obj 2. tasm al prg[.asm] Hatására létrejön az al prg.obj 3. tlink fo prg[.obj], al prg[obj] Hatására létrejön a fo prg.exe Van egy még kényelmesebb módja a szubrutin használatnak, ez pedig a saját könyvtár létrehozása. Tételezzük fel, hogy a cnts és a prst szubrutinjainkat ellátuk a szükséges PUBLIC direktívával, és kimentettük ôket az f cnts.asm illetve az f prst.asm állományokba Ezekután a következôket kell tennünk : 1. tasm f cnts[.asm] Hatására létrejön a f cnts.obj 2. tasm f prst[.asm] Hatására létrejön az f prst.obj 3. tlib bt.lib +f cnts[obj] +f prst[obj], btlst Hatására
létrejön a bt.lib könyvtár és a btlst lista állomány, ami a könytárban lévô modulok neveit tartalmazza. A könyvtár egy olyan állomány, amely objekt modulokat tartalmaz, megfelelô formátumban. A TLIBEXE a Borland C++ 31-hez adott könyvtárkészítô program és BIN alkönyvtárban található. Ezt a könytárat hozzálinkelhetjük - és a példaprogramok készítése során hozzá is linkeltem - a fôprogramunk objekt moduljához. Az EXTRN és PUBLIC direktívák használata itt is kötelezô ! A linkelést a következôképpen kell végrehajtani : tlink fo prg[.obj] , , , bt[lib] A [] zárójelek között szereplô karakterek opcionálisak ! 74 9.4 Kész szubrutinok szerkesztése, saját könyvtárak létrehozása Van egy olyan módja is a szubrutin használatnak, amikor a szubrutinjainkat egy szöveges állományba gyűjtjük, és a fôprogramban az INCLUDE direktívával beinzertáljuk. Meglehetôsen növeli a méretet Ezekután nézzük meg az integer alsó
és felsô byte-ját megcserélô program különbôzô verzióit. Lesz olyan verzió, ahol a szubrutinok forrásszövege része a fôprogramnak, és lesz olyan is ahol a szubrutin modulokat a bt.lib könyvtárból linkeltem a fôprogramhoz. A most következô példaprogramban a két szubrutin forrásszöveg szinten része a programnak. A paraméterátadás regisztereken keresztül történik. 15. sz példaprogram : ; Paraméterátadás regiszteren keresztül ; .MODEL small .STACK 100h .DATA szam DW 1 ide DB 5 DUP (20h) vege LABEL BYTE ; Itt jelöljük meg az DB 0dh,0ah,$ ; IDE utáni byte-ot. ; .CODE prgstart: mov ax,@data mov ds,ax ; xor ax,ax mov ax,[szam] mov BYTE PTR[szam],ah ; Ez a két sor a csere mov BYTE PTR[szam+1],al ; mov ax,[szam] ; Átadjuk a számot mov bx,OFFSET vege - 1 mov cx,5 call cnts mov bx, OFFSET ide call prst ; Itt adjuk át a kon; vertálási terület ; végének a címét ; Átadjuk a kiiratási ; szélességet ; Meghívjuk a konver; táló szubrutint ;
Átadjuk a konvertá; lási terület kezdô; címét ; Meghívjuk a kiiró 75 9. Szubrutinok szervezése ; szubrutint ; Visszaadjuk a vezér; lést a DOS-nak. mov ah,4ch int 21h ; ; Itt jön a CNTS függvény ; ; AX : Konvertálandó szám ; DS:BX : A string végére mutató pointer, ahol ; elhelyezzük a számot ; CX : A konvertálás szélessége ; cnts PROC mov si,10 cnvlp: sub dx,dx div si add dl,0 mov [bx],dl dec bx and ax,ax jz szokoz loop cnvlp szokoz: ret cnts ; Most konvertáltuk karakterré ENDP ; Itt jön a PRST függvény ; ; DS:BX : A stringre mutató pointer ; prst PROC push ax push dx ; mov ah,9 mov dx,bx int 21h pop dx pop ax ret prst ENDP END prgstart 76 9.4 Kész szubrutinok szerkesztése, saját könyvtárak létrehozása A következô példaprogram egyrészt az INCLUDE direktíva használatát mutatja be. Az INC DEFASM fileban különbözô konstansokhoz rendeltünk szimbólumokat. Másrészt itt találkozunk elôször azzal a megoldással, hogy a
program nem tartalmazza azon szubrutinoknak a forrásszövegét, amelyekre hivatkozik. A 94 alatt leírtak szerint elkészítettem a btlib állományt, és ezt linkeltem a fôprogram objekt moduljához. Az END direktíva használata is új elemmel bôvül : a program indításakor a mögé írt címkére adódik a vezérlés. Jelen esetben ez a prgstart címke. 16. sz példaprogram: Nézzük elôször az INC DEF.ASM állomány tartalmát : ; ; INC DEF.ASM ; ; CR EQU CHARIN EQU CHAROUT EQU STROUT EQU RETDOS EQU : Include fileként használható assembly program 0dh 1 2 9 4ch ; Újsor ; Egy karakter bekérése ; Egy karakter kiírása ; String kiírása ; visszatérés a DOS-hoz Ezután nézzük a programot : ; Hivatkozott szubrutinok a BT.LIB - ben .MODEL small .STACK 100h .DATA INCLUDE INC DEF.ASM szam DW 1 ide DB 5 DUP (20h) vege LABEL BYTE DB 0dh,0ah,$ ; .CODE EXTRN cnts:PROC ; A szubrutinok deklarálása EXTRN prst:PROC ; extern-ként prgstart: mov ax,@data mov ds,ax ; xor
ax,ax mov ax,[szam] 77 9. Szubrutinok szervezése mov BYTE PTR[szam],ah ; Ez a két sor a csere mov BYTE PTR[szam+1],al ; mov ax,[szam] mov bx,OFFSET vege - 1 mov cx,5 call cnts mov bx, OFFSET ide call prst mov ah,RETDOS ; Az INC DEF.ASM-bôl int 21h END prgstart A következô példaprogramban az integer megfordítását is szubrutinként implementáljuk. Az átadási módszer : változó címe regiszterben 17. sz példaprogram: ; Megvalósitás függvénnyel ; ; Paraméterátadás : Változó címe a BX regiszterben .MODEL small .STACK 100h .DATA szam DW 1 ide DB 5 DUP (20h) vege LABEL BYTE DB 0dh,0ah,$ ; .CODE EXTRN cnts:PROC EXTRN prst:PROC prgstart: mov ax,@data mov ds,ax ; mov bx,OFFSET szam ; BX-ben átadjuk a megfor; dítani kívánt integer ; címét call fordit ; Meghívjuk a szubrutint mov ax,[szam] mov bx,OFFSET vege - 1 mov cx,5 call cnts mov bx, OFFSET ide call prst mov ah,4ch int 21h 78 9.4 Kész szubrutinok szerkesztése, saját könyvtárak létrehozása ;
; Itt jön a FORDIT megvalósítása ; ; BX : Megforditandó szám címe ; fordit PROC ; mov ax,[bx] ; AX-be töltjük a BX-ben ; lévô címen található ; adatot mov BYTE PTR [bx],ah ; Ez a két sor a csere mov BYTE PTR [bx+1],al ret fordit ENDP END A következô példaprogramban megváltozik a paraméterátadás módja. A változó címét nem egy regiszteren keresztül, hanem a stacken keresztül adjuk át 18. sz példaprogram: ; Paraméterátadás : Változó címe a STACK-ben .MODEL small .STACK 100h .DATA ; szam DW 1 ide DB 5 DUP (20h) vege LABEL BYTE DB 0dh,0ah,$ ; .CODE EXTRN cnts:PROC EXTRN prst:PROC prgstart: mov ax,@data mov ds,ax ; mov ax,OFFSET szam ; push ax ; Itt adjuk át a változó címét ! call fordit add sp,2 ; Helyre állítjuk a STACK-et ! ; mov ax,[szam] mov bx,OFFSET vege - 1 mov cx,5 call cnts mov bx, OFFSET ide 79 9. Szubrutinok szervezése call prst mov ah,4ch int 21h ; ; Itt jön a fordit megvalósítása ; ; Megfordítandó szám címe a STACK-ben
; fordit PROC push bp mov bp,sp mov bx,[bp+4] ; BP+0 = bp értéke ; BP+2 = visszatérési cím ; BP+4 = paraméter címe ; BX-ben a szám címe van ! mov ax,[bx] ; AX-ben a szám van, az indirekció miatt mov BYTE PTR [bx],ah mov BYTE PTR [bx+1],al ; Ez a két sor a csere pop bp ret fordit ENDP END Végül következzék egy elrettentô példa a globális változó használatára. 19. sz példaprogram : ; Paraméterátadás : globális változó használata .MODEL small .STACK 100h .DATA ; szam DW 1 ide DB 5 DUP (20h) vege LABEL BYTE DB 0dh,0ah,$ ; .CODE EXTRN cnts:PROC EXTRN prst:PROC ; prgstart: mov ax,@data mov ds,ax ; 80 9.5 Makró alkalmazás call fordit ; Semmilyen paraméterátadás nincs ; mov ax,[szam] mov bx,OFFSET vege - 1 mov cx,5 call cnts mov bx, OFFSET ide call prst ; mov ah,4ch int 21h ; ; Itt jön a fordit megvalósítása ; ; fordit PROC ; mov ax,[szam] ; AX-ben a szám van ; mov BYTE PTR [szam],ah ; Ez a két sor a csere mov BYTE PTR [szam+1],al ; ret ;
fordit ENDP END 9.5 M akr ó alkalmazás A makró használatra csak egy nagyon rövid példát mutatok be jelezvén, hogy ilyen lehetôség is rendelkezésünkre áll. A makró definíciója a következô : név MACRO . . . utasítások . . . ENDM formális paraméterlista 81 9. Szubrutinok szervezése A makróra egyszerűen a nevével hivatkozunk. Példa : dosint MACRO intszam ; mov ah,intszam int 21h ; ENDM . . . Hívása : dosint 4ch ; visszatérés a DOS-hoz A makró és a szubrutin annyiban hasonlítanak egymásra, hogy mindegyiket csak egyszer kell megírni és többször lehet hivatkozni rájuk. Ezzel a hasonlóság be is fejezôdött ! Amíg a szubrutint vezérlésátadással tudjuk használni, addig a makrónak - a fordítás során - a forrásszövege másolódik be a hivatkozás helyére. ! Világos, hogy a szubrutin csökkenti a program méretét, de a vezérlés átadás miatt növeli annak végrehajtási idejét. A makró viszont csökkenti a
végrehajtási idôt, mert nem kell vezérlésátadással foglalkozni, de növeli a program méretét. A feladattól függôen kompromisszumot kell kötni a két alkalmazás között ! A most következô példaprogram két karakter összehasonlítását végzi. A DOS hívásokat makró alkalmazással oldja meg. 20. sz példaprogram : .MODEL small .STACK 100h ; Itt jön a makró definíció ! ; dosint MACRO intnum mov ah,intnum int 21h ENDM .DATA nagy12 db 1 > 2,$ nagy21 db 1 < 2,$ egy12 db 1 = 2,$ sorem db 0dh,0ah,$ 82 Ellenôrzô kérdések és feladatok .CODE mov ax,@data mov ds,ax ; dosint 1 ; A makró meghivása egy karakter ; beolvasására mov cl,al dosint 1 mov bl,al ; A másik karakter beolvasása ; mov al,cl cmp al,bl je egyen js n2 1 ; mov dx,OFFSET nagy12 jmp kiir egyen: mov dx,OFFSET egy12 jmp kiir n2 1: mov dx,OFFSET nagy21 kiir: mov ah,9 int 21h ; mov ah,2 mov dl,cl int 21h mov dl,20h int 21h mov dl,bl int 21h mov ah,9 mov dx,OFFSET sorem int 21h dosint
4ch ; Viszatérés a DOS-hoj funkció END Ellenôr zô kér dések és feladatok 01. Mit nevezünk szubrutinnak, miért elônyös a használata ? 02. Milyen formában kell megírni egy szubrutint, és mely utasítással kell hivatkozni rá ? 83 9. Szubrutinok szervezése 03. Miért nem elég szubrutinhíváskor csak a vezérlésátadást megvalósítani? 04. Mi játszódik le egy szubrutin meghívásakor illetve a szubrutinba való visszatéréskor ? 05. Milyen paraméterátadási módokat ismer. Vezesse le a stack kezelést két paraméter stacken keresztüli átadása esetén. Adja meg az elsô paraméter indirekt címét valamint a másodikét is. ( A [bp+] alakra gondolok ). 06. Irjon programot, amely az 56. oldalon található 07 sz feladatot szubrutin alkamazásával oldja meg. A két string címét regisztereken keresztül adja át a szubrutinnak ! 07. Olja meg a 06. sz feladatot úgy, hogy a két string címét a stacken keresztül adja át ! 08. Bôvítse
úgy a 07. számú programot, hogy alkalmas legyen két string viszonyának megállapítására akkor is, ha azok nem egyenlô hosszúak. 09. Irjon programot, amely az 56. oldalon található 09 sz feladatot szubrutin alkamazásával oldja meg. A két string címét elôször regisztereken, majd a stacken keresztül adja át a szubrutinnak ! 10. Irjon programot, amely az 56. oldalon található 10 sz feladatot szubrutin alkamazásával oldja meg. A két string címét elôször regisztereken, majd a stacken keresztül adja át a szubrutinnak ! 11. Irja meg a string másoló programot szubrutinnal. A paraméterátadás módja szabadon választott ! 12. Képezze két 10 elemű integer vektor skaláris szorzatát. A szorzat eredményét a most ne a TD-vel tekintse meg, hanem irassa ki a képernyôre! A feladatot szubrutin alkalmazásával oldja meg, a paraméterátadás módját Önre bízom ! 13. Irja meg a string hosszát meghatározó programot szubrutinnal. A
paraméterátadás módja szabadon választott ! A stirng összehasonlító, stringmásoló és stringhossz meghatározó rutinokból készítsen - a tanult módon - egy SAJAT.LIB nevű könyvtárat ! 84 10. Str ing utasítások, DOS és BI OS szolgáltatások Ebben fejezetben elôször az i8086-os stringutasításait mutatom be, amelyek csak a nevükben stringutasítások hiszen integer változókon is működnek. Az utasítások közül az elsô három adatmozgató, a következô kettô pedig összehasonlító. Ezután a stringműveleteknél használható prefixek működését nézzük meg. A stringműveletek után példaprogramok következnek a DOS dátumelôállító, illetve a BIOS színkezelô szolgáltatásaira 10.1 A L ODS utasítás Szintaxis : LODS <cím> , LODSB , LODSW A lods utasítás egy vagy két byte-ot tölt be a memóriából az al vagy az ax regiszterbe. Az aktuális memóriacím a DS:SI regiszterpárban kell, hogy rendelkezésre álljon.
További fontos momentum, hogy a lods a DF értékétôl függôen eggyel vagy kettôvel növeli vagy csökkenti az SI regiszter értékét. Amennyiben DF = 0 akkor növeli - a string művelet a növekvô indexek mentén hajtódik végre -, amennyiben DF = 1 úgy csökkenti. Az utasítás egyetlen flaget sem állít A lods utasítást háromféleképpen alkalmazhatjuk : 1. lods <cím> Itt a <cím> csak a változótípus ( db vagy dw ) megállapítására szolgál, hiszen a DS:SI regiszterpár már tartalmazza a címet. 2. lodsb Egy byte betöltése a DS:SI-ben lévô címrôl az al regiszterbe. Az SI regisztert eggyel növeli vagy csökkenti DF értékétôl függôen. 3. lodsw K ét byte betöltése a DS:SI-ben lévô címrôl az ax regiszterbe. Az SI regisztert kettôvel növeli vagy csökkenti DF értékétôl függôen. ∑ 10. Str ing utasítások, DOS és BI OS szolgáltatások A lods utasítás alkalmazására a késôbbiekben láthatunk példaprogramot !
10.2 A STOS utasítás Szintaxis : STOS <cím> , STOSB , STOSW A stos utasítás egy vagy két byte-ot ír ki az al vagy az ax regiszterbôl az ES:DI regiszterpárban megadott címre. A DF értékétôl függôen eggyel vagy kettôvel növeli vagy csökkenti a DI regiszter értékét. Az utasítás szintén nem állít egyetlen flaget sem. A stos utasítást is háromféleképpen alkalmazhatjuk : 1. stos es:<cím> Itt az es:<cím> csak a változótípus (db vagy dw) megállapítására szolgál, hiszen az ES:DI regiszterpár már tartalmazza a címet. 2. stosb Egy byte kiírása az ES:DI-ben lévô címre az al regiszterbôl. A DI regisztert eggyel növeli vagy csökkenti DF értékétôl függôen. 3. stosw K ét byte kiírása az ES:DI-ben lévô címre az ax regiszterbôl. A DI regisztert kettôvel növeli vagy csökkenti DF értékétôl függôen. A következô példaprogramban a lods és a stos utasítások használatát mutatom be egy
stringmásoló program keretén belül. Mindkét utasításna az 1 számú alakját - vagyis a cím operandusos alakot - használjuk. Mielôtt áttekintenénk a programot elôször nézzük meg, mit jelent a $ szimbólum. Egy jelentését már ismerjük : string lezáró karakterként használtuk aposztrófok között Itt a $ jel azonban aposztrófok nélkül áll és az elhelyezkedés számláló szerepét tölti be, vagyis annak a memóriacímnek az értékét tartalmazza, ahol áll Ezért tudjuk a programunk 6 sorában az innen nevű string hosszának meghatározására használni.A másik megemlítendô a SEG operátor Ez az operátor az utána írt memóriaváltozó szegmenscímét állítja elô ! 21. sz példaprogram: 84 10.2 A STOS utasítás .MODEL small .STACK 100h .DATA innen db Ezt a szöveget másoljuk !,0 SLEN EQU ($-innen) ; Az EQU az egyenlôség direktíva ide db SLEN DUP(?) db 0dh,0ah,$ ; .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás
string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN masol: lods [innen] stos es:[ide] loop masol ; xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; mov ah,4ch int 21h END A következô példaprogram ugyanezt a műveletet hajtja végre, csak a lods és a stos 2. számú alakjával ( lodsb, stosb ) 22. sz példaprogram: .MODEL small .STACK 100h .DATA innen db Ezt a szöveget másoljuk !,0 SLEN EQU ($-innen) ide db SLEN DUP(?) 85 10. Str ing utasítások, DOS és BI OS szolgáltatások db 0dh,0ah,$ ; .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide cld ; Növekvô indexek irányában ; masol: lodsb stosb cmp al,0 jnz masol ; xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; mov ah,4ch int 21h END A következô példaprogram csökkenô indexek mentén
hajt végre másolást. A string vége vizsgálatot most nem a nullával való összehasonlítással oldottam meg, hanem az SLEN értékét CX-be írva loop ciklust szerveztem. 23. sz példaprogram: .MODEL small .STACK 100h .DATA ; innen db Ezt a szöveget másoljuk !,0 SLEN EQU ($-innen) ide db SLEN DUP(?) db 0dh,0ah,$ ; .CODE 86 10.2 A STOS utasítás mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen + SLEN - 1 ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide + SLEN - 1 ; std ; Csökkenô indexek irányában ; mov cx,SLEN masol: lodsb stosb loop masol ; xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; mov ah,4ch int 21h END A következô két példaprogram integer vektor másolását mutatja be. Az elsô a címoperandusos, a második az operandusnélküli alakot használja. 24. sz példaprogram: .MODEL small .STACK 100h .DATA ; innen dw 1,2,3,4,5 SLEN EQU ($-innen)/2 ide dw SLEN DUP(?)
; .CODE mov ax,@data mov ds,ax ; ; Az integer hossza miatt 87 10. Str ing utasítások, DOS és BI OS szolgáltatások mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN cld masol: lods [innen] stos es:[ide] loop masol ; mov ah,4ch int 21h END 25. sz példaprogram: .MODEL small .STACK 100h .DATA innen dw 1,2,3,4,5 SLEN EQU ($-innen)/2 ide dw SLEN DUP(?) ; .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN cld masol: lodsw 88 10.3 A MOVS utasítás stosw loop masol ; mov ah,4ch int 21h END 10.3 A M OVS utasítás Szintaxis : MOVS <cím> , MOVSB , MOVSW A movs utasítás tulajdonképpen a lods és a stos utasítások összevonása. Egy vagy két byte-ot olvas az al vagy ax
regiszterbe a DS:SI címrôl, majd a DF értékétôl függôen növeli vagy csökkenti SI regiszter értékét. Ezután az al vagy ax regiszterbôl ( ahova az olvasás történt ) kiírja a tartalmat az ES:DI-ben lévô címre és a DI értékét is a megfelelô módon változtatja. Tulajdonképpen egy byte-ot vagy egy szót másol ! Közbensô regisztert nem használ, egyetlen flag értékét sem állítja. Szintén háromféleképpen használható : 1. movs es:<cél címe>, <forrás címe> A címek szintén csak a változótípus (db vagy dw) megállapítására szolgálnak. 2. movsb Egy byte olvasása a DS:SI-ben lévô címrôl az al regiszterbe. Az al tartalmának kiírása az ES:DI-ben lévô címre. AZ SI, DI regiszterek a DF-tôl függôen eggyel nônek vagy csökkennek. 3. movsw K ét byte olvasása a DS:SI-ben lévô címrôl az ax regiszterbe. Az ax tartalmának kiírása az ES:DI-ben lévô címre. AZ SI, DI regiszterek a DF-tôl függôen kettôvel nônek
vagy csökkennek. 26. sz példaprogram: .MODEL small .STACK 100h 89 10. Str ing utasítások, DOS és BI OS szolgáltatások .DATA ; innen db Ezt a szöveget másoljuk !,0 SLEN EQU ($-innen) ide db SLEN DUP(?) db 0dh,0ah,$ ; .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN ; A forrás string hossza CX-ben cld ; A D flaget nullázzuk, igy a növekvô ; indexek felé mozog a másolás ; masol: ; movsb ; A MOVSB egyik regiszterben sem ; helyezi el a byte-ot, igy a hosszát ; ismernünk kell ! loop masol ; xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; mov ah,4ch int 21h end 10.4 A SCAS utasítás Szintaxis : 90 SCASB , SCASW 10.5 A CMPS utasítás A scas utasítás az ES:DI-ben lévô címen tárolt byte vagy szó értékét hasonlítja össze az al vagy az ax regiszter tartalmával. Az összehasonlítás kivonás
formájában történik meg, de az eredményt nem ôrzi meg a művelet, hanem csak a flageket változtatja az eredménynek megfelelôen. Ezek a flagek a következôk : CF, PF, AF, ZF, SF, OF. A művelet után a DI regisztert a megfelelô módon változtatja az utasítás. Amennyiben byte-os összehasonlítást akarunk végezni úgy a scasb alakot, amennyiben szavas összehasonlítást, úgy a scasw alakot használjuk. 10.5 A CM PS utasítás Szintaxis : CMPSB , CMPSW A cmps utasítás a DS:SI-ben lévô címen lévô byte vagy szó értékét hasonlítja össze az ES:SI-ben tárolt címen lévô byte vagy szó tartalmával. Az érintett flagek a következôk : CF, PF, AF, ZF, SF, OF. A művelet után az SI és a DI regisztereket a megfelelô módon változtatja az utasítás. Amennyiben byte-os összehasonlítást akarunk végezni úgy a cmpsb alakot, amennyiben szavas összehasonlítást, úgy a cmpsw alakot használjuk. ! Az összes stringutasításra érvényes szabály
tehát az, hogy az SI és a DI regisz-terek tartalma az adott művelet elvégzése után mindig a következô fel-dolgozandó elemre mutat ! Mielôtt példaprogramokat mutatnék az elôzô két utasításra, tekintsük át a string utasításokkal kapcsolatos prefixeket ! 10.6 Str ing utasítás pr efixek : REP, REPE, REPNE A string utasítás prefixek az utasítások ismétlését idézik elô. Csakis és kizárólag string utasításokhoz használhatók ! A r ep addig ismétli a string utasítást, amíg a CX regiszter tartalma el nem éri a nullát. 91 10. Str ing utasítások, DOS és BI OS szolgáltatások A r epe ( repz ) addig ismétli a string utasítást, amíg a CX regiszter tartalma el nem éri a nullát és ZF értéke 1. Ennélfogva csak a scas és cmps utasításokkal együtt alkalmazható ! A r epne ( repnz ) addig ismétli a string utasítást, amíg a CX regiszter tartalma el nem éri a nullát és ZF értéke 0. Szintén csak a scas és cmps utasításokkal
együtt alkalmazható ! Ezek után következzenek a példaprogramok ! A következô példaprogram még mindig a stringmásolást hajtja végre, de most már prefix használattal. A másolást TD alatt lehet ellenôrizni, ugyanis a programok a kiiratást nem tartalmazzák ! 27. sz példaprogram: .MODEL small .STACK 100h .DATA innen db Ezt a szöveget másoljuk !,0 SLEN EQU ($-innen) ide db SLEN DUP(?) db 0dh,0ah,$ .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN ; A forrás string hossza CX-ben cld ; A D flaget nullázzuk, igy a növekvô ; indexek felé mozog a másolás ; rep movsb ; A MOVSB egyik regiszterben sem he; lyezi el a byte-ot, igy a hosszát kell ! ; xor ax,ax mov ah,9 mov dx,OFFSET ide int 21h ; 92 ; ismernünk 10.6 String utasítás prefixek : REP, REPE, REPNE mov ah,4ch int 21h END A következô két
példaprogram a movs két alakját és a rep prefixet használja. 28. sz példaprogram: .MODEL small .STACK 100h .DATA innen dw 1,2,3,4,5 SLEN EQU ($-innen)/2 ide dw 5 DUP(0) .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; A forrás string címe a DS:SI-ben mov si,OFFSET innen ; mov ax,SEG ide mov es,ax ; A cél string címe az ES:DI-ben mov di,OFFSET ide ; mov cx,SLEN ; A forrás string hossza CX-ben cld ; rep movs es:[ide],[innen] ; xor ax,ax mov bx,0 mov cx,SLEN mutat: ; A másolás ellenôrzése TD alatt mov ax,ide[bx] inc bx inc bx loop mutat ; mov ah,4ch int 21h END 93 10. Str ing utasítások, DOS és BI OS szolgáltatások 29. sz példaprogram: .MODEL small .STACK 100h .DATA innen dw 1,2,3,4,5 SLEN EQU ($-innen)/2 ide dw 5 DUP(0) .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax mov si,OFFSET innen ; mov ax,SEG ide mov es,ax mov di,OFFSET ide ; mov cx,SLEN cld ; rep movsw ; xor ax,ax mov bx,0 mov cx,SLEN ; mutat: mov ax,ide[bx] inc bx inc bx loop
mutat ; mov ah,4ch int 21h END A következô példaprogram a scas, az ezt követô pedig a cmps utasításra mutat példát, prefixhasználattal együtt. A scas utasítást arra használjuk, hogy egy stringben megkeressük az elsô szóközt és a di regiszterben tároljuk annak pozícióját. A cmps utasítással egy stringben keresünk egy adott karakter mintát A találati poziciókat a TD-vel lehet ellenôrizni ! A cmps utasítást bemutató 94 10.6 String utasítás prefixek : REP, REPE, REPNE programban találunk egy új utasítást, ez pedig a lea utasítás. Az utána következô operandus offszetjét állítja elô csakúgy mint az OFFSET operátor Ez utóbbit azonban tömb elemek offszetjének elôállítására nem használhatjuk, míg a lea utasítást igen ! 30. sz példaprogram: .MODEL small .STACK 100h .DATA ; innen db Ebben a szövegbenkeressük az elsôszóközt! SLEN EQU ($-innen) ; .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov es,ax ; A string címe az
ES:DI-ben mov di,OFFSET innen ; mov cx,SLEN ; A string hossza CX-ben cld mov al,20h ; A szóköz kódja repne scasb ; Keresés nem egyenlô - ig ! ; jne vege dec di ; Itt volt az elsô szóköz ! ; vege: mov ah,4ch int 21h END 31. sz példaprogram: .MODEL small .STACK 100h .DATA ; minta db Ezaz ml EQU $ - minta innen db 111122223333Ezaz4444 SLEN EQU ($-innen)/ml ; 95 10. Str ing utasítások, DOS és BI OS szolgáltatások .CODE mov ax,@data mov ds,ax ; mov ax,SEG innen mov ds,ax ; mov ax,SEG minta mov es,ax ; A keresô tábla szegmense a DS-ben ; A minta szegmense ES-ben cld ; mov dx,SLEN mov bx,0 ; kov: lea si,innen[bx] ; A tábla aktuális offszetje ; az SI-ben lea di,minta ; A minta offszetje DI-ben mov cx,ml ; A minta mérete CX-ben repe cmpsb ; Keresés amíg a tábla elem = ; = minta elem ! ; je megvan add bx,ml ; Ha nincs találat, akkor arrébb ; megyünk a minta hosszával dec dx jnz kov mov si,ml ; megvan: sub si,ml ; Itt volt az egyezés ! ; mov ah,4ch int
21h END 10.7 DOS szolgáltatás hívását bemutató példapr ogr am A kérdéses DOS szolgáltatás a dátum elôállító függvény lesz, sorszáma 2ah. Ennek a szolgáltatásnak bemenô paramétere nincs. Kimenô paraméterei a következôk : 96 10.7 DOS szolgáltatás hívását bemutató példaprogram CX regiszter DH regiszter DL regiszter AL regiszter = Az évszám négy számjegyen = A hónap két számjegyen = A nap két számjegyen = A hét hányadik napja 1 számjegyen Az AL regiszterel kapcsolatban meg kell jegyezni, hogy a vasárnap a 0. a hétfô az 1. stb sorszámozęs van érvényben Ezekután a feladat az, hogy írassuk ki a dátumot magyar szokás szerint, de a hónapot és a hét napját ne a sorszámával irassuk ki, hanem a nevével. Például : 1997. június 11 szerda 32. sz példaprogram : .MODEL small .STACK 100h .DATA ; ev DW ? honap DB ? nap DB ? hnap DB ? sev DB 4 DUP (20h) sevv LABEL BYTE DB $ snap DB 2 DUP (20h) snv LABEL BYTE DB $ spa DB ;
holen DB 12 ; hok DB Január ,$ DB Február ,$ DB Március ,$ DB Április ,$ DB Május ,$ DB Június ,$ DB Július ,$ DB Augusztus ,$ DB Szeptember ,$ DB Október ,$ DB November ,$ DB December ,$ ; 97 10. Str ing utasítások, DOS és BI OS szolgáltatások naplen DB 11 ; napok DB Vasárnap ,$ DB Hétfô ,$ DB Kedd ,$ DB Szerda ,$ DB Csütörtök ,$ DB Péntek ,$ DB Szombat ,$ sorem DB 0dh,0ah,$ ; .CODE prgstart: mov ax,@data mov ds,ax ; xor ax,ax ; mov ah,2ah int 21h ; Meghivtuk a dátum függvényt ; mov [ev],cx ; Kimentjük a dátum alkotó részeit mov [honap],dh mov [nap],dl mov [hnap],al ; mov ax,[ev] ; Az évszám átalakítása kezdôdik ! mov bx,OFFSET sevv - 1 mov cx,4 call cnts ; xor ax,ax ; A nap átalakítása kezdôdik ! mov al,[nap] mov bx,OFFSET snv - 1 mov cx,2 call cnts ; mov bx, OFFSET sev ; Az évszám kiírása call prst mov ah,2 ; Egy szóközt írunk ki utána mov dl,[spa] int 21h ; xor ax,ax ; A hónap nevének szöveges kiírása mov al,[honap] ;
kezdôdik dec al mul holen mov bx,OFFSET hok add ax,bx 98 10.7 DOS szolgáltatás hívását bemutató példaprogram mov dx,ax mov ah,9 int 21h ; mov bx, OFFSET snap ; A nap kiirása call prst mov ah,2 ; Egy szóközt írunk ki utána mov dl,[spa] int 21h ; xor ax,ax ; A hét napjának szöveges kiirása mov al,[hnap] ; kezdôdik mul naplen mov bx,OFFSET napok add ax,bx mov dx,ax mov ah,9 int 21h ; mov dx,OFFSET sorem int 21h mov ah,4ch int 21h ; cnts PROC mov si,10 cnvlp: sub dx,dx div si add dl,0 mov [bx],dl dec bx and ax,ax jz szokoz loop cnvlp szokoz: ret cnts ENDP ; ; prst PROC push ax push dx mov ah,9 mov dx,bx int 21h pop dx pop ax ret prst ENDP 99 10. Str ing utasítások, DOS és BI OS szolgáltatások END prgstart 10.8 BI OS szolgáltatás hívását bemutató példapr ogr am A következô program cilusban ír ki egyszerre egy karaktert szín attribútummal. Az attribútumot ciklusban változtatja 0 - 255 ig Ezt a 10h BIOS szolgáltatás 9-es funkciójával
tudjuk végrehajtani ! Minden kiírás után egy billentyű lenyomására vár a program. 33. sz példaprogram: .MODEL small .STACK 100h .DATA .CODE mov ax,@data mov ds,ax ; mov ah,9 mov al,A mov bh,0 mov cx,2 ; BIOS funkció sorszáma -> AH ; Kiiratni kivánt karakter -> AL ; Aktív képernyô lap -> BH ; Hányszor írjuk ki -> CX ; mov dx,0 kiir: mov bl,dl int 10h push ax mov ah,1 int 21h pop ax inc dx cmp dx,255 jnz kiir ; mov ah,4ch int 21h END 100 ; Attribútum -> DL ; Karakter attribútum ; Várakozás billentyű lenyomásra Ellenôrzô kérdések és feladatok A DOS és a BIOS szolgáltatások bemutatásával meglehetôsen sok szakkönyv foglalkozik. PC-n a Norton Guide nevű on-line help taglalja részletesen a szolgáltatások paraméterezését, és funkcióját. Ellenôr zô kér dések és feladatok 01. Hogyan működnek a lods, stos, movs utasítások ? 02. Milyen alakjaik léteznek ? Miért elônyôs a használatuk a hagyományos
adatmozgató utasítások használatához képest ? 03. Mire alkalmasak a scas és cmps uatsítások ? 04. Irjon programot, amely egy maximum 70 karakter hosszú szövegben megszámolja a magánhangzók számát ! A megvalósíthoz stringutasítást használjon ! 05. Irjon programot, amely meghatározza egy C típusú string hosszát ! A megvalósíthoz stringutasítást használjon ! 06. Irjon programot amely a 10h BIOS szolgáltatás 02 funkcióját hívja. Ennek segítségével tudjuk a képernyôn a kurzort pozicionálni. Az AH regiszterbe kell írni a 2-ôt, a BH-ba a képernyô lapszámot ( 0 ), DH-ba a sor, DL-be az oszlop számot. Ha a program működik, készítsen önálló kurzorpozicionáló szubrutint, és adja hozzá a meglévô saját könyvtárához ! 101 11. A C nyelv és az assembly nyelv kapcsolata Σ A kapcsolattartást a két nyelv között többek közt az indokolja, hogy manapság - hála a C nyelv elterjedésének - egyre ritkább a nagyméretű,
tisztán assembly nyelven írt program. A fejlesztés általában úgy történik, hogy csak a nagy sebességigényű programrészeket “ hegyezik ki” assembly betétekkel, a többit leginkább C - esetleg Pascal - nyelven írják meg. Ez a fejezet konkrétan az inline kapcsolatot és a modul szerű kapcsolatot mutatja be. 11.1 I nline kapcsolat Az inline kapcsolat onnan kapta a nevét, hogy az assembly forrásszöveg r észe a C nyelvű forrásszövegnek, akár soronként is váltogathatják egymást. A C programban elôforduló assembly sort az asm kulcszóval kezdjük. Példa: . . . int i; . . . i=0; asm dec WORD PTR i; i++; Ha valaki egy kicsit is jártas az inline assembly használatában az azonnal rájön, hogy i értéke 0 marad az utasítások végén, de azért nézzük meg részletesebben az asm kulcsszóval kezdôdô sort. 1. Azontúl, hogy megállapítjuk hogy asm kulcsszóval kezdôdik, megállapíthatjuk, hogy a ‘ ;’ karakterrel végzôdik. Ezenkívül
az inline assembly sor végzôdhet még ‘ új sor’ billentyűvel is. Ha több assembly sort akarunk egymásután elhelyezni, akkor az “ asm“ után a ‘ { ‘ karaktert kell írni még ugyanabba a sorba, majd utána soronként az utasításokat, végül a ‘ } ’ karakterrel kell zárni az assembly sorokat. 11.1 Inline kapcsolat 2. Az a tény, hogy ‘ ;’ - re is végzôdhet a sor meggátol bennünket abban, hogy a ‘ ;’ karaktert megjegyzésként használjuk, mint ahogy eddig tettük. Inline assembly-t tartalmazó C programban csak a már ismert karaktereket használhatjuk megjegyzésként ! ( /* . * / illetve // . ) 3. Mint látható szabadon hivatkozhatunk a C program valamennyi változójára, sôt konstansára is. Ez a sor az i változó értékét eggyel csökkenti 4. Mivel a dec nem határozza meg a műveleti szélességet, az inline assembly pedig nem készít nyilvántartást arról, hogy az i milyen típusú változó, ezért a műveleti szélességet,
nekünk kellett elôírni. Természetesen, ahol regiszter jelöli ki a műveleti szélességet ott nincs ilyen probléma. Az inline assembly sor általános alakja tehát a következô : asm [<címke>] < utasítás | dir ektíva > < oper andus . > < ; | új sor > Az inline assembly használatával kapcsolatosan még a következôkre kell ügyelnünk : 1. Az inline assembly urgó utasítások csak C címkére vonatkozhatnak. Az összes többi “ asm” utasítás viszont mindenre hivatkozhat, csak C címkére nem. Példa : 2. asm jnz cimke1; . . . cimke: asm cimke2 : asm jnz cimke2; . . . . . . . . . jó nem j ó A következô regiszterek tartalma változatlan kell, hogy maradjon inline assembly használat után : BP, SP, CS, DS, SS. A többi regiszter kezelése a programozóra van bízva. 101 11. A C nyelv és az assembly nyelv kapcsolata 3. Az inline assembly-t tartalmazó C programot a BCC.EXE “ egyenes” fordítóval kell fordítani és
linkelni. Vagy a BCCEXE -B kapcsolóját használjuk, vagy a programunk elején elhelyezzük a #pr agma inline sort. A fordítás ilyenkor a hagyományos C -> OBJ -> EXE sorrend helyett úgy alakul, hogy a C programot elôször assemblyre fordítja le a BCC úgy, hogy az assembly részekhez nem nyúl hozzá. Ezután a BCC elindítja a TASM.EXE programot, ami az assembly programból objekt programot készít, majd a TLINK.EXE programot indítja el ami létrehozza a futtatható programot A fordítás menete tehát a következô : .C -> ASM -> OBJ -> EXE Amennyiben azt szeretnôk látni, hogy a C forrásprogramunknak milyen assembly program felel meg, úgy használjuk a BCC -S kapcsolóját. Példa : bcc -S prog.c hatására létrejön a prog.asm állomány Az inline assembly használat hátrányaként megemlíthetjük, hogy csökkenti az áttekinthetôséget és a hordozhatóságot ! A következô két példaprogram mindegyike string másolást valósít meg. Az elsô a
függvény paraméterek nevét használja. 34. sz példaprogram: #include <stdio.h> #include <conio.h> #include <string.h> #pragma inline void main() { void sm(char * , char , int ); char st1[] = { "Ezt másoljuk !" } ; char st2[] = { "xxxxxxxxxxxxxx" } ; int l; l = strlen(st1); sm( st1,st2,l ); printf(" STRING1 = %s ",st1) ; printf(" STRING2 = %s ",st2) ; 102 11.1 Inline kapcsolat getch() ; } void sm(char * s1, char s2, int len) { asm { // A forrás string OFFSET-je SI-ben mov si,s1 // A cél string OFFSET-je DI-ben mov di,s2 // A forrás string hossza CX-ben mov cx,len } masol: asm { mov al,[si] mov [di],al inc si inc di loop masol } } A második példaprogram szintén stringmásolást hajt végre, de stack címzést használ a paraméterek kezeléséhez. A “ vájtfülüeknek” biztosan feltűnik, hogy hiányzik a BP regiszter elmentése a stack-be, és a mov bp, sp utasítás is. Ez azért van így, mert a C
automatikusan megcsinálja. Ha a programunkat a -S kapcsolóval assemblyre fordítjuk, akkor ez világosan látszik a listán. 35. sz példaprogram: #include <stdio.h> #include <conio.h> #include <string.h> #pragma inline void main() { void sm(char * , char , int ); char st1[25] = { "Ezt másoljuk !" } ; char st2[] = { " " }; int l; 103 11. A C nyelv és az assembly nyelv kapcsolata l = strlen(st1); sm( st1,st2,l ); printf(" STRING1 = %s ",st1) ; printf(" STRING2 = %s ",st2) ; getch() ; } void sm(char * s1, char s2, int len) { asm { //A forrás string OFFSET-je SI-ben mov si,[bp+4] // A cél string OFFSET-je DI-ben mov di,[bp+6] // A forrás string hossza CX-ben mov cx,[bp+8] } masol: asm { mov al,[si] mov [di],al inc si inc di loop masol } } 11.2 M odul kapcsolat Ellentétben az inline kapcsolattal, modul kapcsolat esetén forrásnyelvi szinten nincs közös része a C illetve az assembly nyelvű programnak. ! Az
elsô dolog amire ügyelni kell, hogy a C nyelvű modul is és az assembly nyelvű modul is ugyanazt a memória modellt kell, hogy használja. Esetünkben ezt a BCC -ms kapcsolójával garantálhatjuk. Az általunk használt egyszerűsített szegmensdirektívák BCC kompatibilis szegmenseket generálnak, úgyhogy továbbra is használhatóak A második dolog az assembly modul használatának formai részével kapcsolatos. Az assembly rutint a C forrásprogramban EXTERN-ként kell deklarálni Az assembly modulban a PUBLIC direktívát kell használni a szubrutin forrás104 11.2 Modul kapcsolat szövege elôtt. A szubrutin neve az ‘ ’ ( aláhúzás ) karakterrel kell, hogy kezdôdjön - utalva arra, hogy C nyelvű a hívóprogram ! A harmadik dolog amit meg kell vizsgálnunk, az a C nyelvű modul és az assembly nyelvű modul közti paraméterátadás. A C a paramétereit mindig a stacken keresztül adja át mégpedig olyan sorrendben, hogy a jobb szélsô para-métert helyezi
el elôször a stackben, és utána a többit. Enek az az eredménye, hogy az elsônek átadott paraméter ( a bal szélsô ) lesz legközelebb a stack tetejéhez. Példa : . EXTERN szubr2( int, int, int ); int a, b, c; . szubr2( a, b, c ); // Assembly nyelvű szubrutin . A stack állapota hívás után : OFFSET R. A a b c Mint eddig is az OFFSET R. A a visszatérési cím ( Return Address ) offset címe. Ha most a szubrutinban relatív stack címzéssel akarjuk elérni az elôbb C-bôl átadott paramétereket, akkor ezt a következôképpen tehetjük meg : Példa : PUBLIC szubr2 szubr2 szubr2 PROC push bp mov bp, sp mov ax,[bp+4] ; Az a paraméter AX-be kerül mov bx,[bp+6] ; A b paraméter BX-be kerül mov cx,[bp+8] ; A c paraméter CX-be kerül . . ; utasítások . ENDP END A szubrutin hívás után a stacket a hívó program állítja helyre ! Esetünkben tehát ez azt jelenti, hogy a C hívási mechanizmus lesz az, amelyik elvégzi az add sp,6 utasítást ! 105
11. A C nyelv és az assembly nyelv kapcsolata A negyedik dolog a regiszterek mentésének kérdése. Már az inline kapcsolatnál láttuk, hogy van öt regiszter, amelyeknek az értékét a C ugyanúgy akarja visszakapni, amilyen tartalommal átadta az assemblynek. Ez a helyzet itt sem változik. Ez az öt regiszter továbbra is a BP, SP, CS, DS, SS A többi általános célú regisztert szabadon használhatjuk. Az ötödik egyben utolsó kérdés a szubrutin által visszaadott érték kérdése. A mi esetünkben - mivel csak db és dw adattípust használunk - az AL vagy AX regiszterben kell elhelyezni a visszatérési értéket ! A C illetve az assembly nyelvű forrásmodul fordítása és linkelése pusztán egy sor begépelését igényli a részünkrôl : Példa : Legyen egy prg1.c és egy prg2asm nevű programunk Ekkor a következô sort kell begépelnünk : bcc [-ms] prg1.c prg2asm A [] zárójelpárban szereplô kapcsoló opcionális. A C nyelvű illetve az assembly nyelvű
modulnak nem lehet azonos a neve ! Meg kell még válaszolnunk egy kérdést ! Mi van akkor, ha valaki az objek-tum orientált programozás és az assembly nyelvű modul használat örömeit akarja élvezni ? Ebben az esetben .CPP kiterjesztést kell használnia, és ilyenkor a fordításnál a függvénynevek megváltoznak. Ezért valamilyen módon közölnünk kell a fordítóval, hogy az assembly rutinunk nevét hagyományos C konvenció szerint fordítsa, azaz ne változtassa meg. Ez a konstrukció a következô : ? Példa : A szubr3 egy assemblyben megvalósított rutin és .CPP függvénybôl szeretnénk használni extern “ C” { extern szubr3( int, int * , int); } A következô példaprogram a stringmásolást valósítja meg, de most önálló C és assembly nyelvű modulok formájában. 36. sz példaprogram: Elôször a C nyelvű modul listája látható : 106 11.2 Modul kapcsolat #include <stdio.h> #include <conio.h> #include <string.h> //
String másolás C fôprogram + önálló assembly modul extern void sm(char * , char , int ); void main() { char st1[25] = { "Ezt másoljuk !" } ; char st2[25] = { "xxxxxxxxxxxxxx" } ; int l; l = strlen(st1); sm( st1,st2,l ); printf(" STRING1 = %s ",st1) ; printf(" STRING2 = %s ",st2) ; getch() ; } Majd ezt követi az assembly nyelvű modul : ; ; String másolás szubrutinként megvalósítva ; .MODEL small .STACK 100h .DATA ; .CODE PUBLIC sm sm PROC ; push bp mov bp,sp ; mov si,[bp+4] ; A forrás string OFFSET-je SI-ben 107 11. A C nyelv és az assembly nyelv kapcsolata mov di,[bp+6] ; A cél string OFFSET-je DI-ben mov cx,[bp+8] ; A forrás string hossza CX-ben ; masol: mov al,[si] mov [di],al inc si inc di loop masol ; sm pop bp ret ENDP END A következô példaprogram az értékvisszaadást szemlélteti egy C típusú string hosszának meghatározása kapcsán. 37. sz példaprogram: #include <stdio.h> #include
<conio.h> // Stringhossz meghatározás void main() { extern int sh(char * ); char st1[25] = { "Mennyi lehet a hossza ?" } ; int l; l = sh(st1); sh( st1 ); printf(" STRING1 = %s ",st1) ; printf(" HOSSZ = %d ",l) ; getch() ; } 108 11.2 Modul kapcsolat ; ; String hossza szubrutinként megvalósítva ; .MODEL small .STACK 100h ;.DATA ; .CODE PUBLIC sh sh PROC ; push bp mov bp,sp ; mov cx,0 ; Itt számoljuk a hosszat mov si,[bp+4] ; A forrás string OFFSET-je SI-ben ; hossz: mov al,[si] cmp al,0 je vege inc si inc cx jmp hossz ; vege: pop bp mov ax,cx ret sh ENDP END A következô példaprogram a jól ismert integer alsó felsô byte megfordítást mutatja be, de .CPP kiterjesztésű programból hívott assembly modul segítségével. 38. sz példaprogram: 109 11. A C nyelv és az assembly nyelv kapcsolata #include <stdio.h> #include <conio.h> // Integer forditás extern "C" { extern void fordit(int * ); } void
main() { int i; printf(" Kérem I értékét : ") ; scanf("%d",& i) ; fordit( & i ); printf(" I = %d ",i) ; getch() ; } ; ; Integer megforditását végzô assembly szubrutin ; .MODEL small .STACK 100h .CODE PUBLIC fordit fordit PROC ; push bp mov bp,sp ; mov bx,[bp+4] ; mov ax,[bx] mov BYTE PTR [bx],ah mov BYTE PTR [bx+1],al ; pop bp ret fordit ENDP END 110 ; A bázispointer elmentése a stackba ; A szám cime a BX - ben ! ; Maga a szám az AX-ben ! ; Ez a ket sor a csere Ellenôrzô kérdések és feladatok Ellenôr zô kér dések és feladatok 01. Melyek a C - assembly kapcsolattartás módjai, és mi a különbség köztük ? 02. Mi az inline kapcsolat lényege ? 03. Mi a modul kapcsolat formai és tartalmi lényege ? 04. Hogyan valósul meg a C paraméterátadási konvenció ? 05. Irjon programot amely összefűz két C típusú stringet. A két stringet Cben olvassuk be, magát az összefűzést pedig assemblyben
végezzük el Oldja meg a feladatot elôször inline assemblyben, majd a modul kapcsolat segítségével ! 06. Irjon programot, amely egy 5x5-ös mátrixot helyben tükröz a fôátlójára ! A mátrixot C-ben lehet inicializálni, a tükrözést assemblyben kell elvégezni. Oldja meg a feladatot elôször inline assemblyben, majd a modul kapcsolat segítségével ! 07. Tegye inteligensebbé a 06. feladat megoldását ! Irja át úgy a programot, hogy ne csak 5x5-ös, hanem bármilyen mátrix tükrözésére alkalmas legyen. Jelezzen hibát, ha a mátrix nem négyzetes ! 08. Irjon programot amely eldönti egy 5x5-ös mátrixról, hogy szimmetrikus-e ! Egy mátrix akkor szimmetrikus, ha aij = aj i minden i,j-re. A mátrixot C-ben lehet inicializálni, a vizsgálatot assemblyben kell elvégezni. Oldja meg a feladatot elôször inline assemblyben, majd a modul kapcsolat segítségével ! 09. Irjon egy egyszerűsített morze jeleket elôállító programot ! A program bemenete egy
karakter, amely csak számjegy lehet ! Ezt a karaktert Cben kell bekérni, majd át kell adni egy assembly rutinnak. Ez a rutin elôször vizsgálja le, hogy valóban számjegy érkezett, majd írja ki a képernyôre a számjegynek megfelelô morze kódot ! A program kérdezzen rá, hogy akarunk-e még morzézni és ha igenlô a válasz ( i vagy I ), akkor olvassa be a következô karaktert ! A C - assembly kapcsolat módja szabadon választott !A számjegyeknek megfelelô morze kódok a következôk : 1 = .---6 = - 2 = .--7 = -- 3=.-8 = --- 4=.9 = ---- 5=. 0 = ----- 111 11. A C nyelv és az assembly nyelv kapcsolata 10. 112 Irjon programot, amely visszafejti a bemenô adatként kapott ötelemű morze kódból a neki megfelelô számjegyet ! A visszafejtést assemblyben végezze el ! 12. Néhány szó a fej lettebb pr ocesszor ok utasításair ól Ebben fejezetben azokat a lényeges különbségeket mutatom be amelyek a tanult i8086-os utasítások és ugyanezen
utasítások fejlettebb processzorokon történô megvalósításai között állnak fenn. Szó lesz továbbá a skálázott címzésrôl is A programok 386-os üzemmódban készültek ( az TASM assembler ezen verziójának idejében még nem terjedt el a Pentium ) de az utasítások ugyanígy szerepelnek a Pentium utasításkészletében is ! 12.1 Regiszter készlet, új memór iacímzési mód A 80386-os processzor és fejlettebb társai 32 bites regiszterekkel rendelkeznek, csak a szegmensregiszterek maradtak meg 16 bitesnek. A 32 bites regiszterek megszokott nevei elé egy ‘ E’ betű kerül, tehát az AX regiszterbôl EAX regiszter lesz. Természetesen az alsó 16 bitre ezután is lehet AX néven hivatkozni, ennek alsó 8 bitjére AL, felsô 8 bitjére pedig AH névvel. A felsô 16 bitre nem lehet külön nével hivatkozni, de megfelelô rotáló utasítással az alsó 16 bit helyére lehet rotálni, majd a kívánt művelet elvégzése után vissza lehet rotálni az eredeti
helyére. EAX 31 15 0 AH AL AX Ez a felosztás igaz az eddigi BX, CX, DX illetve új nevükön az EBX, ECX, EDX regiszterekre is ! Azok a regiszterek emelyek eddig sem voltak felezhetôk, azok most sem lettek azok. Az alsó 16 bitre a régi névvel lehet hivatkozni, például az ESI regiszter alsó 16 bitjének a neve továbbra is SI. ESI 31 15 0 SI ∑ 12. Néhány szó a fejlettebb processzorok utasításairól Ez a megállapítás igaz, az ESI, EDI, EBP, ESP, EIP és az EF regiszterekre. A szegmensregiszterek tehát maradtak 16 bitesek, viszont belép két új szegmensregiszter, az FS és a GS. Ezek az ES regiszterhez hasonlóan felhasználói adatszegmens kijelölésére és kezelésére alkalmasak. Az új memória címzési módot skálázott címzésnek hívják. Azt jelenti, hogy bármely indexregiszter tartalmát eggyel, kettôvel, néggyel illetve nyolccal lehet szorozni. Nyilván annak a változónak a típusa határozza meg a szorzótényezôt, amit
indexelünk. Látható tehát, hogy a fejletteb processzoroknál elmozdulás történt - egy fokkal legalábbis - a pointeraritmetika irányába. Fontos könnyítés még, hogy mindegyik általános célú 32 bites regiszter lehet bázisregiszter és az ESP regisztert kivéve bármelyik lehet indexregiszter. Ha visszaemlékezünk, akkor az i8086-os processzornál csak a BX illetve BP lehetett bázisregiszter és az SI illetve DI lehetett indexregiszter. Ezek után a megkülönböztetési szabály a következô : 1. Egy regiszter használata esetén ez a regiszter a bázisregiszter. 2. Két regiszter használata esetén a baloldali lesz a bázisregiszter, a jobboldali lesz az indexregiszter. 3. Skálázott címzés használata esetén a skálázott regiszter lesz az indexregiszter, bárhol is áll. A kérdés többek között azért sem közömbös, mert például az EBP regiszter bázisregiszterként a stackszegmenst, indexregiszterként az adatszegmenst címzi ! A következô
példaprogram egy 3x3-as mátrix indexelését és kiiratását mutatja be a skálázott címzés felhasználásával. A 80386-os üzemódot a 386 direktíva használatával érjük el. Ha ezt a direktívát a MODEL elé írnánk akkor 32 bites szegmenseket kapnánk. A MODEL után írva a hagyományos 16 bites szegmensekkel dolgozunk A program fordítása hagyományosan történik, a linkelésnél a /3 kapcsolót kell alkalmaznunk : tlink /3 filenévobj 39. sz példaprogram: .MODEL small .386 .STACK 100h .DATA 112 ; 16 bites szegmensek használata 12.1 Regiszterkészlet, új memóriacímzési mód matrix DW 1,2,3 DW 4,5,6 DW 7,8,9 selem DB 5 dup(20h) selemv LABEL BYTE DB $ elemsz DW 3 sorem DB 13,10,$ ; ; .CODE EXTRN cnts:PROC EXTRN prst:PROC ; prgs: mov ax,@data mov ds,ax ; mov cx,0 cikl1: mov edi,0 kiir : mov al,cl mul BYTE PTR [elemsz] movzx ebx,ax shl bx,1 ; 2-vel való szorzás a DW miatt ; Kiiratás következik mov ax,matrix[ebx][edi* 2] mov bx,OFFSET selemv - 1 push cx ;
El mentjük a ciklus változót mov cx,3 call cnts pop cx ; Visszahozzuk a ciklus változót mov bx,OFFSET selem call prst inc di cmp di,[elemsz] jnz kiir ; mov ah,9 mov dx,OFFSET sorem int 21h inc cx cmp cx,[elemsz] jnz cikl1 ; mov ah,9 mov dx,OFFSET sorem 113 12. Néhány szó a fejlettebb processzorok utasításairól int 21h ; mov ah,4ch int 21h END prgs 12.2 Új utasítások, új ver ziók - A léptetô és rotáló utasításoknál megszűnt az a megkötés, hogy a konstans értéke csak 1 lehet. - A mov utasítás zéró és elôjel kiterjesztéssel rendelkezik : movzx és movsx. Míg az i8086-os processzornál tilos volt 8 bites értéket 16 bites regiszterbe írni, itt a movzx használatával ez lehetségessé válik. A movzx alkalmazásával a 8 (16) bites érték a 16 (32) bites cél változó alsó 8 (16) bitjére íródik, a felsô 8 (16) bit kinullázódik. A másik kiterjesztés elôjeles másolást engedélyez Példa : i8086-os processzor esetén a mov bx,dl
használata tilos. i80386-os processzor esetén a movzx bx,dl érvényes művelet. Példa : i8086-os processzor esetén egy elôjeles byte-ot, a következôképpen lehetett egy 16 bites regiszterbe írni : . mov al,[byte] cbw mov dx,ax . ; AX-be konvertáljuk elôjelesen i80386-os processzor esetén ugyanez : . mov sx dx,[byte] 114 12.2 Új utasítások, új verziók . - A loop utasításnak megfelelô új utasítás a loopd. Ez az utasítás az ECX regiszterrel működik együtt. A loope utasításnak a loopde a loopne utasításnak a loopdne felel meg. Működésük logikája megegyezik - A meglévô stringutasításokhoz újak társulnak : a lods új alakja a lodsd, a stos új alakja stosd, movs utasításé a movsd. Az öszehasonlító utasítások új alakjai : scasd és cmpsd. - Az újdonságok ismertetését az imul utasítás új verzióinak ismertetésével fejezem be. Az imul utasításnak két sôt három operandusa is lehet, az alábbiak szerint : 1.
Lehetôség van közvetlen konstanssal való szorzásra 2. Bármely regiszter szorozható bármely regiszterrel, illetve memóriaváltozóval. 3. A szorzás eredménye külön célregiszterben tárolható. Ilyenkor a célregiszter az elsô operandus, a második az egyik szorzótényezô, a harmadik pedig a másik szorzótényezô. A harmadik operandus csak konstans lehet ! Nézzük meg a következô példaprogram segítségével az imul utasítás különbôzô verzióinak működését. Az eredmények megtekintéséhez a TD-t használjuk ! 40. sz példaprogram: .MODEL small .386 .STACK 100h .DATA ; szorzo1 dw 2 szorzo2 dw 3 szorzat dw 0 ; .CODE ; 16 bites szegmensek 115 Függelék : A tanult utasítások összefoglalása mov ax,@data mov ds,ax ; mov cx,[szorzo1] imul cx,[szorzo2] imul cx,2 ; imul cx,[szorzo1],[szorzo2] Ezt nem fogadja el ! ; imul [szorzo1],[szorzo2] Ezt meg nem hajtja vegre ! mov ax,[szorzo2] imul dx,ax,2 imul cx,[szorzo1],3 shl cx,4 mov [szorzat],cx ;
mov ah,4ch int 21h END Függelék : A tanult utasítások összefoglalása Adatmozgató, ar itmetikai utasítások Utasítás Működés leírása Érintett flagek MOV op1, op2 ADD op1,op2 ADC op1,op2 SUB op1,op2 SBB op1,op2 CMP op1,op2 INC op DEC op NEG op CBW CWD MUL op IMUL op op1 = op2 op1 = op1 + op2 op1 = op1 +op2 + [CF] op1 = op1 - op2 op1 = op1 - op2 - [CF] op1 - op2 op = op + 1 op = op - 1 op = -op AX <- AL DX:AX <- AX AX = AL* op AX = AL* op (elôjeles) C,P,A,Z,S,O C,P,A,Z,S,O C,P,A,Z,S,O C,P,A,Z,S,O C,P,A,Z,S,O P,A,Z,S,O P,A,Z,S,O C,P,A,Z,S,O C,O C,O 116 Logikai utasítások DIV op IDIV op XCHG op1, op2 AL = AX/op AL = AX/op (elôjeles) op1 <--> op2 Határozatlan Határozatlan - L ogikai utasítások Utasítás Működés leírása AND TEST OR XOR NOT op1 = op1 AND op2 op1 AND op2 op1 = op1 OR op2 op1 = op2 XOR op2 op = NOT op op1,op2 op1,op2 op1,op2 op1,op2 op Érintett flagek ; bitenként ; bitenként ; bitenként ; bitenként ; bitenként
C=0,P,Z,S,O=0 C=0,P,Z,S,O=0 C=0,P,Z,S,O=0 C=0,P,Z,S,O=0 A,P,Z,S,O Ugr ó utasítások Feltételnélküli ugró utasítás Utasítás Működés leírása Érintett flagek JMP címke IP = IP + OFFSET címke vagy IP = IP + Eltolás - Feltételes ugró utasítások Elôjelnélküli mennyiségekkel kapcsolatos ugró utasítások : CMP op1,op2 Utasítás ; op1 és op2 elôjelnélküli mennyiségek Működés leírása Érintett flagek 117 Függelék : A tanult utasítások összefoglalása JA cimke ugrás ha op1 > op2 JB cimke ugrás ha op1 < op2 JE cimke ugrás ha op1 = op2 JAE cimke ugrás ha op1 >= op2 JBE cimke ugrás ha op1 <= op2 JNA cimke ugrás ha op1 <= op2 JNB cimke ugrás ha op1 >= op2 JNE cimke ugrás ha op1 != op2 JNAE cimke ugrás ha op1 < op2 JNBE cimke ugrás ha op1 > op2 - Elôjeles mennyiségekkel kapcsolatos ugró utasítások CMP op1,op2 Utasítás ; op1 és op2 elôjeles mennyiségek Működés leírása JG cimke ugrás ha op1
> op2 JL cimke ugrás ha op1 < op2 JE cimke ugrás ha op1 = op2 JGE cimke ugrás ha op1 >= op2 JLE cimke ugrás ha op1 <= op2 JNG cimke ugrás ha op1 <= op2 JNL cimke ugrás ha op1 >= op2 JNE cimke ugrás ha op1 != op2 JNGE cimke ugrás ha op1 < op2 JNLE cimke ugrás ha op1 > op2 Érintett flagek - Flagek értékétôl függô ugró utasítások Utasítás Működés leírása Érintett flagek JC JS JO JZ JP JPE JPO JNC JNS JNO JNZ JNP JNPE ugrás, ha CF = 1 ugrás, ha SF = 1 ugrás, ha OF = 1 ugrás, ha ZF = 1 ugrás, ha PF = 1 ugrás, ha PF = 1 ugrás, ha PF = 0 ugrás, ha CF = 0 ugrás, ha SF = 0 ugrás, ha OF = 0 ugrás, ha ZF = 0 ugrás, ha PF = 0 ugrás, ha PF = 0 - 118 cimke cimke cimke cimke cimke cimke cimke cimke cimke cimke cimke cimke cimke Ciklus szervezô utasítások JNPO cimke ugrás, ha PF = 1 - Ciklus szer vezô utasítások Utasítás Működés leírása Érintett flagek LOOPcímke Ha CX > 0, akkor ugrás a címkére
- LOOPE címke (LOOPZ) Ha CX >0 és ZF = 1 akkor ugrás a címkére - LOOPNE címke (LOOPNZ) Ha CX >0 és ZF = 0 akkor ugrás a címkére - JCXZ címke Ha CX=0 akkor ugrás a címére - L éptetô és r otáló utasítások Utasítás Működés leírása Érintett flagek SHL, SAL op1,op2 SHR op1, op2 SAR op1, op2 ROL op1, op2 RCL op1,op2 ROR op1,op2 RCR op1,op2 Bitléptetés balra op2-vel Bitléptetés jobbra op2-vel Aritmetikai bitléptetés jobbra op2-vel Bitrotáció balra op2-vel Bitrotáció balra a CF-en keresztül Bitrotáció jobbra op2-vel Bitrotáció jobbra a CF-en keresztül C,P,Z,S,O C,P,Z,S,O C,P,Z,S,O C,O C,O C,O C,O Feltételbit ( flag ) műveletek Utasítás Működés leírása Érintett flagek CLC CMC STC CLD STD CLI STI CF = 0 CF = NOT CF CF = 1 DF = 0 DF = 1 IF = 0 IF = 1 C=0 C C=1 D=0 D=1 I=0 I=1 119 Irodalomjegyzék Szubr utin szer vezô uatsítások Utasítás Működés leírása CALL címke RET Vezérlésátadás cimke
nevű szubrutinra Visszatérés szubrutinból - Érintett flagek Ver em (stack) műveletek Utasítás Működés leírása PUSH op POP op Két byte írása a stack tetejére op-ból Két byte olvasása a stack tetejérôl op-ba - Érintett flagek Str ingműveletek Utasítás LODS STOS MOVS SCAS CMPS REP REPE (REPZ) REPNE (REPNZ) Működés leírása Egy byte/szó olvasása [SI]-rôl AL/AX-be Egy byte/szó írása [DI]-re AL/AX-ból Egy byte/szó olvasása [SI]-rôl és kiírása [DI]-re Az ES:DI-n lévô byte/szó értékét C,P,A,Z,S,O hasonlítja össze AL/AX tartalmával Az ES:DI-n lévô byte/szó értékét C,P,A,Z,S,O hasonlítja össze a DS:SI-n tárolt byte/szó tartalmával Addig ismétlôdik az utasítás, amíg CX != 0 Ismétlôdés amíg CX != 0 és ZF = 1 Ismétlôdés amíg CX != 0 és ZF = 0 I r odalomj egyzék A felhasznált irodalom a következô : 120 Érintett flagek - Stringműveletek 1. Dr. Gidófalvi Zoltán : Az IBM PC programozása assembly
nyelven. Novotrade 1988 2. BORLAND Turbo Assembler User’ s Guide Version 1.0 Borland International 1988, 1989 3. Grochmann-Eicher : A 8086/88-as mikroprocesszor. Technika és programozás. Data Becker - Novotrade 1987 121 TARTAL OM JEGYZÉK Elôszó .7 Bevezetés . 8 1. Az i8086-os processzor memóriakezelése és regiszterei 11 1.1 Memóriakezelés 11 1.2 Az i8086-os regiszterei 12 1.3 Az alkalmazott software eszközök 16 Ellenôrzô kérdések és feladatok . 17 2. Egyszerûsített direktívák, fôbb adattípusok, alapvetô DOS szolgáltatások 18 2.1 A HELLOASM ismertetése 18 2.2 További DOS szolgáltatások ismertetése 21 2.3 Alapvetô adattípusok és definiálásuk 21 Ellenôrzô kérdések és feladatok .23 3. Aritmetikai utasítások 24 3.1 Az i8086-os processzor általános utasításformátuma 24 3.2 A MOV utasítás 25 3.3 Az ADD és ADC utasítások 25 3.4 A SUB és SBB utasítások 26 3.5 A CMP utasítás 27 3.6 Az INC utasítás 27 3.7 A
DEC utasítás 28 3.8 A NEG utasítás 28 3.9 A CBW és CWD utasítások 28 3.10 A MUL és IMUL utasítások 29 3.11 A DIV és IDIV utasítások 29 3.12 Az XCHG utasítás 30 3.13 Az OFFSET operátor 30 Ellenôrzô kérdések és feladatok . 31 4. Logikai utasítások 32 4.1 Az AND és a TEST utasítások 32 4.2 Az OR utasítás 33 4.3 Az XOR utasítás 34 4.4 A NOT utasítás 34 Ellenôrzô kérdések és feladatok . 35 5. Feltételnélküli és feltételes ugró utasítások 36 5.1 Feltételnélküli ugró utasítás : jmp 36 5.2 Feltételes ugró utasítások 37 5.21 Elôjel nélküli mennyiségekkel kapcsolatos utasítások 37 3 5.22 Elôjeles mennyiségekkel kapcsolatos utasítások 38 5.23 Flagek értékétôl függô utasítások 38 Ellenôrzô kérdések és feladatok . 41 6. Ciklusszervezô utasítások 42 6.1 A loop utasítás 42 6.2 A loope és a loopne utasítások 43 6.3 A Turbo Debugger használata 44 Ellenôrzô kérdések és feladatok . 47 7.
Memória címzési módok 48 7.1 Közvetlen operandusú címzés 48 7.2 Direkt címzés 48 7.3 Indirekt címzés 48 7.4 Indexelt címzés 50 Ellenôrzô kérdések és feladatok . 56 8. Léptetô és flagekkel kapcsolatos utasítások 57 8.1 Az SHL, és SAL utasítások 57 8.2 Az SHR és SAR utasítások 58 8.3 A ROL és RCL utasítások 59 8.4 A ROR és RCR utasítások 60 8.5 Flagekkel kapcsolatos utasítások 61 Ellenôrzô kérdések és feladatok . 63 9. Szubrutinok szervezése 9.1 A szubrutin formai szervezése és hívása 9.2 A szubrutinhívás és visszatérés mûködése 9.3 A paraméter át - és visszaadás kérdése 9.31 A PUSH és a POP utasítások 9.4 Kész szubrutinok szerkesztése, saját könyvtárak létrehozása 9.5 Makró alkalmazás Ellenôrzô kérdések és feladatok . 64 64 65 66 67 72 80 82 10. String utasítások, DOS és BIOS szolgáltatások 83 10.1 A LODS utasítás 83 10.2 A STOS utasítás 84 10.3 A MOVS utasítás 88 10.4 A
SCAS utasítás 90 10.5 A CMPS utasítás 90 10.6 String utasítás prefixek : REP, REPE, REPNE 91 10.7 DOS szolgáltatás hívását bemutató példaprogram 95 10.8 BIOS szolgáltatás hívását bemutató példaprogram 98 Ellenôrzô kérdések és feladatok .99 11. A C nyelv és az assembly nyelv kapcsolata 100 11.1 Inline kapcsolat 100 4 11.2 Modul kapcsolat 104 Ellenôrzô kérdések és feladatok . 110 12. Néhány szó a fejlettebb processzorok utasításairól 111 12.1 Regiszterkészlet, új memória címzési mód 111 12.2 Új utasítások, új verziók 114 Függelék : A tanult utasítások összefoglalása . 116 PÉL DAPROGRAM OK JEGYZÉK E 01. Üzenet kiiratása képernyôre 18 02. Bit maszkolás 33 03. Két karakter összehasonlítása 40 04. Integer alsó és felsô byte-jának megfordítása 44 05. Számjegyek számlálása egy max 70 hosszú karaktersorozatban 46 06. Kisbetûs karaktersorozat átalakítása nagybetûssé 51 07. Két 10 elemû
integer vektor összeadása 52 08. C típusú string hosszának megállapítása 53 09. C típusú string másolása 54 10. 3x3-as karaktermátrix indexelése és kiiratása 55 11. Hamming távolság kiszámítása 61 12. Soros protokol szimulálása 62 13. A PUSH és a POP utasítások mûködése 67 14. Integer kiiratása szubrutinnal 70 15. Integer alsó és felsô byte-jának megfordítása, kiiratás szubrutinnal 74 16. Ugyanaz mint 15 , de a szubrutinok egy saját könyvtárban vannak 76 17. Ugyanaz mint 15 , de a megfordítást is szubrutin végzi 77 18. Ugyanaz mint 17 , de a paraméterátadás a stacken keresztül történik 78 19. Ugyanaz mint 17 , de globális változó használattal 79 20. Egyszerû makró alkalmazás 81 21. String másolás a LODS és a STOS utasítások címmel történô alkalmazásával 84 22. String másolás a LODSB és a STOSB utasítások alkalmazásával 85 23. Ugyanaz mint 22 , de csökkenô indexek mentén 86 24. Integer másolás
a LODS és a STOS utasítások címmel történô alkalmazásával 87 25. Integer másolás a LODSW és a STOSW utasítások alkalmazásával 88 26. String másolás a MOVSB utasítás alkalmazásával 89 27. String másolás a REP prefix, és a MOVSB utasítás alkalmazásával 91 28. Integer másolás a REP prefix és a MOVS címmel történô alkalmazásával 92 29. Integer másolás a REP prefix és a MOVSW utasítás alkalmazásával 93 30. Szóköz keresése szövegben a SCASB utasítás alkalmazásával 94 31. Szövegminta keresése szövegben a CMPSB utasítás alkalmazásával 94 32. A DOS dátumelôállító ( 2ah ) függvénye mûködésének bemutatása 96 33. A BIOS képernyôkezelô ( 10h ) szolgáltatása 09h funkciójának bemutatása 98 34. Stringmásolás inline assembly betéttel, változónév használattal 102 5 35. Ugyanaz mint 34 , de a paraméterekre stack címzéssel hivatkozunk 103 36. Stringmásolás megoldása C és assembly modul kapcsolat
segítségével 106 37. C típusú string hosszának meghatározása modul kapcsolat segítségével 107 38. Integer alsó és felsô byte-jának megfordítása CPP kiterjesztésû programból 109 39. 3x3-as mátrix indexelése skálázott címzéssel 112 40. Szorzás változatok az IMUL utasítás új alakjaival 115 6