ÚÄÄÄ ÚÄÄÄ
ÚÄ ÚÄ
ÚÄ ÚÄ ÚÄ
ÚÄ ÚÄÚÄ ÚÄÚÄ
ÚÄ ÚÄ ÚÄ
ÚÄ ÚÄÄ
ÚÄ
ÚÄÄ ÚÄÄ ÚÄÄ
ÚÄ Ú ÚÄ
ASSEMBLY RULEZ
ÚÄÄÄÄÄÄÄÄÄ ÚÄ
ÚÄ ÚÄ
ÚÄÄ ÚÄÄ ÚÄ
ÚÄ ÚÄ ÚÄ
I.rész
ÚÄÄ ÚÄÄ ÚÄÄÄÄ
ÚÄ ÚÄ
Ezt a cikket azoknak
írtam, akik tudnak már valamit a számítógéprõl, esetleg
tudnak valamilyen programnyelven (ez nagyon jó lenne) és most
úgy érzik itt az idõ egy szinttel feljebb (lejjebb) lépni
és szeretnék megismerni az assembly gyönyörségeit.
A cikk a majdnem kezdõknek szól elsõsorban (legalábbis
ez a rész), ezért ha már tudsz egy kicsit assemblyül,
akkor vagy ne olvasd el (de talán hasznos lehet, ha nem vagy mindennel
tisztában) vagy ne bosszankodj azon, hogy milyen "alap" dolgokat
is leírtam. Vannak akiknek ezek még ismeretlenek. Ja, ha valami
nagy baromságot írtam volna, akkor mielõbb contactolj meg,
hogy felvilágosíthass!
Akkor vágjunk is bele!
Elõször
is mi az az assembly nyelv?
Nos ez a processzor saját belsõ (gépi) nyelve (na jó
nem teljesen az, hanem annak egy emberi formája). Mindenki tudja (vagy
sem), hogy a proci mindenféle csúnya bináris (hát
ez mi a fene?) számokkal dolgozik, ezek írják le az utasításokat
meg az adatokat. Nemtom kinek lenne kedve pár száz bináris
- jobb esetben hexa - számot megtanulva programozni. Erre találták
ki az okosok a mnemonikokat (nem káromkodom, elmondom mi ez). Ezek az
adott gépi utasítás angol nyelv– rövidítései
pl.: ADD - ADDition. Vannak ennél cifrábbak, de egyszerûbbek
is. Most má' csak egy gond van. Ezeket a gép nem hajlandó
megenni. Itt jöttek a még okosabbak és csináltak egy
ASSEMBLERt. Ez semmi más, mint egy fordító program, ami
mint a nevébõl is látszik ASSEMBLY programokat fordít
le az adott op. rendszer alatt futtatható formába. Most akár
neki is álhetnénk életünk elsõ assembly programjának,
csakhogy nem tudunk sem assemblyben sem a fordító által
kért file formátumot nem ismerjük. Sebaj!! Itt vagyok én
és majd úgy ahogy magyarázgatok, amit tudok (egyébként
rengeteget tudok, csak ne lennék ilyen szerény).
SZÁMRENDSZEREK |
Kezdetnek egy kis bináris és hexa aritmetika. Most nagy levegõ, mert itt a nagy pillanat, amikor szükség lehet a suliban tanultakra. Mindenki hallott már a számrendszerekrõl. Ezek közül mi a TIZESt használjuk, de vannak mások is. A föníciaiak a 60-ast használták, mások mást. A számítógép a kettes, vagy más néven BINÁRIS számrendszert érti meg. Ennek egyszerû oka, hogy a gép számára csak 1 vagy 0 létezik. Neki ezekbõl kell felépítenie mindent. Az ember meg azért van, hogy mindent saját magához igazítson, így elnevezte az 1-et IGENnek, a 0-t NEMnek. Persze van negatív logika is, ahol minden fordítva van, de ez más tészta.
Alapvetõ
ismeret, hogy a tizes rendszerben (ezentúl DECIMÁLISan) az egyes
számjegyek un. helyiértékeket foglalnak el. Ezeknek megvan
a saját értékük:
6543=6*10^3
+ 5*10^2 + 4*10^1 + 3*10^0
Vagyis a helyiértékek jobbról balra nõnek
és mindig az elõzõ 10-szeresei. Binárisan is
ugyanígy van, csak ott kétszeresek a helyiértékek
és csak a 0 és 1 számjegyek használhatók.
Íme egy binárisan
leírt szám: 11010101.
Ugye ez így semmit nem mond. Oke, akkor átírjuk decimálisba:
11010101 -> 1*2^7+1*2^6+0*2^5+1*2^4+0*2^3+1*2^2+0*2^1+1*2^0=213.
Amint látható egyszerûen csak a helyiértékével
szorozgattam az ott lévõ számot. Most persze megkérdezed,
ha át kell írni akkor mire jó. Csak arra, hogy megismerd
a gép "gondolkodását" és egy újabb
számrendszert. Ez a tizenhatos (16) vagy HEXADECIMÁLIS rendszer.
Na most ez mire jó? Egy kis matematikai érzékkel gyorsan látható, hogy a 16 a 2 4-ik hatványa. Ennek segítségével négy bináris számjegyet egyetlen hexa számjeggyel írhatunk le. Ehhez persze ismerni kell a hexa számokat. A 0-9 tartományt a hagyományos 0-9 számjegyekkel írjuk, míg a 10-15 részt betûkkel jelöljük. Tehát 10=A, 11=B, 12=C, 13=D, 14=E, 15=F. Most már tudjuk a hexa jegyek értékét, már csak egy példa kell.
Az elõzõ példa alapján az 11010101 bináris számot írom át hexába. Elõször két négyes csoportot gyártunk, ezek 1101 és 0101. Ezeknek könnyû megmondani a hexa megfelelõjét, egy kis gyakorlás után mindenkinek menni fog. Így 1101 ->D és 0101 ->5. Most már leírhatjuk az értéket: D5.
Azért használják
a hexa számokat, mert:
- közel állnak a binárisokhoz,
de mégsem olyan nehezen megjegyezhetõek és hosszúak
- jól tükrözik a gép
belsõ számábrázolását, szemben a decimálisokkal
Egyébként megkérdezhetnétek (ha meg tudnátok), hogy honnan látszik egy számról a típusa. Van erre egy jelölési mód: a hexa számok végére egy h betû kerül, a binárisak végére egy b, míg a decimálisok végére egy d. Persze ez csak a hexánál egyértelmû, mert h betû máshol nincs, de általában ha egy kilométer hosszú szám végén egy b betût látsz és a szám csupa 1 és 0, akkor az valószínûleg bináris, a decimálisok végérõl meg nemes egyszerûséggel elhagyják a jelet. Van persze egy másik mód is, ahol a hexa elé egy $ kerül, míg a binárisok elé egy % (lehet, hogy ez csak a 64-en volt így, a $ most is jó ez biztos). Az assemblerek további heppje, hogy a hexa számok elé egy felesleges 0-t kell tenni, ha az bet–vel kezdõdik. Ezek alapján a 213 az 11010101b vagy 0d5h alakban is írható.
Most pedig elmondom, miért nyaggattalak ezekkel. A számítógépekben az információ alapegysége a BIT, ez angol rövidítés és annyit tesz, hogy BInary digiT, azaz bináris számjegy. Ebbõl már tudjátok, hogy ez csak igen gyér információ tároló, mert egy bitnek csak két állapota lehet: 0 vagy 1. Ezért néhány bitet össze szokás fûzni, mivel így sokkal több lehetõség lesz. A lehetõségek száma pontosan 2^n, ahol n az összefûzött bitek száma. Régebben voltak 4 bites procik, ezek 2^4=16 állapotot tudtakmegkülönböztetni. Manapság már a legprimitívebb proci is 8 bites, de az átlagosak már 32 bitesek, míg a számodra elérhetõ legjobbak 64 biten tárolják az adatokat. Persze kényelmetlen mindig leírni, hogy 8, 16, 32... bites, ezért elnevezték 8 bit együttesét bytenak, 16 bitét szónak (word, W), 32-ét pedig duplaszónak (double word, DW).
Végül
pár szó a kettes komplemensrõl. Csak annyit kell tudnod,
hogy ilyen alakban ábrázolják az elõjeles számokat
a számítógépen. Az ilyen számok pont úgy
néznek ki, mint egy natúr bináris szám, csak a kezelésük
más. Az adott gépi utasítás dönti el, hogy
a szám kettes komplemensként vagy sima bináris számként
értelmezõdik. A kettes kompl. számoknál a legnagyobb
helyiértékû bitet elnevezték elõjelbitnek.
Ha ez 1 akkor a szám negatív, ha 0 akkor pozitív. Csakhogy
nem ám csak úgy negatív, hogy kiszámolom a maradék
bitek értékét és elé rakok egy - jelet. A
valódi értéket például úgy kapod meg,
ha kiszámolod az értékét sima binárisként
és ebbõl levonod a sima binárisként lehetséges
legnagyobb
érték+1 -et. Például: 98h az 10011000b, ez kettes
komplemensként értelmezve negatív így a pontos decimális
értékéhez 98h-ból (152) le kell vonni 0ffh+1-et
(256). Ez -104, vagyis 98h kettes komplemensként értelmezve ennyi.
Ezek után már világos, hogy 0ffh az -1 és 80h az
128 és 0a6h az -90.
REGISZTEREK |
Ezek után következzen a processzor belseje, persze csak annyira amennyire kell. A processzorban a számunkra legfontosabb dolgok a REGISZTERek. Ezek belsõ tárolóegységek. Ezek tárolják a mûveletek eredményeit, a mûveletek adatait és még sok egyebet. A regiszterek hossza procitól függõ, a 8086-ban és a 286-ban 16 bitesek, míg a 386-osban és a 486-ban 32 bitesek. Mivel a 8086 az alapja a PC-nek ezért a 8086 assembly programozását fogom taglalni. Persze késõbb sor kerülhet a többire is.
Lássuk tehát a 8086 regisztereit:
- Van 8 darab általános
regiszter, amikben tárolhatunk adatot, címet vagy amit akarunk.
Ezek a regiszterek: AX, BX, CX, DX, SI, DI, BP, SP. Persze ezek közül
mindnek megvan a saját rendeltetése, amit bizonyos utasítások
esetén el kell látnia. Az SP-t jobb nem buzerálni mert
ez tartalmazza a verem állapotát (a vermet késõbb
mesélem). A többi állítgatásával
nem okozhatunk túl nagy bajt, de azért ideírom, mi
is az alapfunkciójuk, vagyis honnan kapták a nevket.
AX - Accumulator,
ez az elsõdleges adatregiszter
BX
- Base, általában a bázismutató az indexelt
címzéseknél
CX
- Count, ez a számláló jó néhány
utasításban
DX
- Data, ez a másodlagos adatregiszter
SI
- Source Index, bizonyos string utasításoknál a forrás
címe
DI
- Destination Index, ez pedig a cél string címe
BP
- Base Pointer, táblázatokhoz bázismutató
SP - Stack Pointer,
a verem mutatója
Persze bármelyiket lehet használni
másra is ha nagyon kell, csak a proci nem mindegyiket fogadja el
egyes helyeken.
- Van 4 darab szegmensregiszter. Ezek az utasítások
vagy adatok címét határozzák meg. A szegmentálást
majd elmagyarázom részletesen, mert kissé körülményes.
Ezeknek is van nevük.
CS
- Code Segment, az programkód szegmense
DS
- Data Segment, az elsõdleges adatszegmens
ES
- Extra Segment, választható adatszegmens
SS
- Stack Segment, a veremszegmens
- Egy azaz egy darab állapotregiszter van, ami az elvégzett mûvelet eredményérõl ad információt. Ez is majd késõbb (pár sor) fejtem ki. Ennek a neve FLAG regiszter.
- Végül, de nem utolsósorban van egy utasításmutató, ami a CS által meghatározott szegmensben a következõ utasításra mutat. Ennek neve IP azaz Instruction Pointer.
Az AX, BX, CX, DX
regisztereket a processzor fel tudja bontani két 8 bites, azaz byte hosszúságú
regiszterre. Az alsó bytenál az X-et L-lel, a felsõ bytenál
pedig H-val helyettesítik. így az AX két része AL
és AH, a CX-é meg CL és CH. Ez azért van,
mert néha csak 8 biten kell a mûveletet
elvégezni.
Akkor lássuk a FLAG regisztert. Mint már mondtam ez a regiszter az elvégzett mûveletek eredményérõl ad infot. Minden eseményhez egy-egy bit van hozzárendelve, aminek az 1-re állítása jelzi, hogy megtörtént a dolog. Ezek a bitek a következõk:
CF - Carry Flag, jelzi, hogy volt-e átvitel magasabb helyiértékre, vagy kölcsönzés alacsonyabb helyiérték felé. Általában az aritmetikai mûveletek eredményét jelzi. Majd az összeadás és a kivonás utasításnál részletesen leírom a szerepét. A különbözõ megszakítási függvények elõszeretettel használják hibajelzésre. Ez a FLAG 0. bitje.
PF - Parity Flag, a paritást jelzi. Ha az eredmény páros, akkor egyre állítódik. Hogy mikor lesz egy eredmény páros? Ha a benne lévõ egyesek száma páros akkor a szám is páros. Ezt hibakeresésre lehet használni adatátvitelnél. Ez a 2. bit.
AF - Auxiliary carry Flag, ez a segéd átviteljelzõ. Akkor lesz 1, ha a mûvelet közben a byte vagy szó alsó felérõl történt átvitel. Ez majd a BCD (Binary Coded Decimal) aritmetikánál lesz hasznos. Ez vala a 4. bit.
ZF - Zero Flag, akkor lesz egy, ha az eredmény nulla. 6. bit.
SF - Sign Flag, elõjel bit, ami a mûvelet eredményének legmagasabb bitjét tartalmazza. Kettes komplemens számoknál ez jelzi az elõjelet. 7. bit.
TF - Trap Flag, csapda (maradjunk a trapnál)
jelzõ bit. Debugger programok használják elõszeretettel,
mert a proci minden utasításkor átadja a vezérlést
az 1-es megszakításnak, ha a TF=1. Ez pediglen (ki gondolná)
a 8. bit.
IF - Interrupt enable Flag,
ez engedélyezi a megszakításokat. Ha 1 az értéke,
akkor a proci figyelembe veszi a hardvermegszakításokat (pl.:
billentyûzet, idõ, soros port ...). ha 0, akkor nem. Bármit
csinálhatsz, az NMI megszakításokat mindig elfogadja
a proci hiszen ezeknek a neve is Non Maskable Interrupt, azaz nem maszkolható,
nem letiltható. A szoftvermegszakításokat is elfogadja
a proci, hiszen ezt te hívod tudatosan. Ez meg a 9. bit.
DF - Direction Flag, az irányjelzõ bit. A sztringmûveletek irányát határozza meg. Ezekrõl majd késõbb. 10. bit.
OF - Overflow Flag jelzi a túlcsordulást. Akkor állítódik be, ha az eredmény nem fér be a célregiszterbe. Például ha összeadjuk a 7564h és a 98b7h számokat, akkor a végeredmény 10ddfh. Ez nem fér el 16 biten, ezért a 0ddfh-t tároljuk a szó hossz£ regiszterben és az OF-et 1-re állítjuk jelezve, hogy a szám elé még egy 1-est kell képzelni. Majd késõbb kiderül mire jó. Itt a vége a 11. bittel.
A FLAG regiszter
egyébként 16 bites, mint arra már rájöhettél.
A még
nem használt biteket a késõbbi
procik (80286+) használják.
A következõ cikkben egy kicsit szegmentálunk.
Lucifer of ZeroBit