ÚÄÄÄ ÚÄÄÄ
ÚÄ ÚÄ
ÚÄ ÚÄ ÚÄ
ÚÄ ÚÄÚÄ ÚÄÚÄ
ÚÄ ÚÄ ÚÄ
ÚÄ ÚÄÄ
ÚÄ
ÚÄÄ ÚÄÄ ÚÄÄ
ÚÄ Ú ÚÄ ASSEMBLY
RULEZ
ÚÄÄÄÄÄÄÄÄÄ ÚÄ
ÚÄ ÚÄ
ÚÄÄ ÚÄÄ ÚÄ
ÚÄ ÚÄ ÚÄ III.rész
ÚÄÄ ÚÄÄ ÚÄÄÄÄ
ÚÄ ÚÄ
MEGSZAKÍTÁSOK |
A számítógépek egyik legfontosabb tulajdonságukat köszönhetik a megszakításoknak. Nevezetesen azt, hogy reagálni tudnak a külsõ eseményekre (pl.: egy billentyû lenyomása) anélkül, hogy állandóan várni kellene rájuk, így nem pocsékolják feleslegesen a proci idejét.
Mik is azok a megszakítások?
Röviden azt lehet mondani, hogy minden olyan esemény megszakítás,
aminek hatására a proci abbahagyja amit addig csinált és
elkezd a megszakítás okozójával foglalkozni. Persze
ez egy nagyon általános megfogalmazás, de kezdetnek megteszi.
A PC-ben háromféle dolog válthat ki megszakítást:
- Külsõ hardver elem adatot akar küldeni vagy fogadni (pl.: billentyûzet) vagy csak jelezni akar valamit (pl.: óra megszakítás). Ezek a HARDVER megszakítások
- A program egy megszakítást hív meg, hogy elvégeztessen egy feladatot ezek tekinthetõk a legfontosabbaknak, hiszen ilyen módon lehet az operációs rendszerrel társalogni. Ezek a SZOFTVER megszakítások
- A proci belsõ megszakításai (kivételek), amelyeket akkor generál a proci, ha egy általa nem ismert (pl.: ismeretlen utasítás) dolgot kérünk tõle vagy valami más a procin belül történt dolog (pl.: INT 03 azaz hibakeresõ utasítás észlelése) az operációs rendszer segítségét kéri. Ezzel a megszakítás fajtával egy mezei valós üzemmódban (majd elmondom mi ez, de csak röviden) programozó gyakorlatilag nem, de még a védett üzemmódban programozók is csak ritkán találkoznak.
Na akkor itt teszünk egy kis kitérõt. Az elõbb hivatkoztam a valós meg a védett üzemmódra. Ezek csak a 80286 vagy fejlettebb procikon vannak. Valós üzemmódban a proci úgy mûködik mint egy 8086, csak gyorsabb. Tehát az összes 8086-ra írt program fut rajta. Védett módban megnyílnak a proci plusz képességei felé vezetõ utak, de ezt legfeljebb a cikksorozat végén taglalom majd bõvebben.
Vissza a megszakításokhoz!
A megszakítások magukban semmit nem érnek. Kell hozzájuk
kiszolgáló rutinokat írni. Ezek fogják elvégezni
a szükséges feladatokat. Példul a billentyûzet megszakítás-kezelõje
lekérdezi a lenyomott billentyût, és a megfelelõ
kódot elhelyezi egy tárolóban.
Nézzük
elõször a szoftver megszakításokat, mivel ezek nélkül
nem tudjuk elérni a DOS "szolgáltatásait".
A szoftver megszakítások nem mások, mint olyan rutinok,
amelyeknek nem ismerjük a pontos memóriabeli helyét, csak
egy sorszámmal hivatkozunk rájuk. Ezt a sorszámot az INT
utasítással adjuk át a procinak, mire õ elindítja
a megfelelõ rutint.
INT utasítás:
Használat:
INT megszakítás_sorszáma
A megszakítás_sorszáma egy szám, az ennek megfelelõ
rutint hívja meg a proci.
Példa: INT
21h ; Ez a DOS funkciókat hívja meg
INT
10h ; A képernyõkezelõ rutinok gyûjteménye
Bizonyára feltûnt, hogy a példában rutingyûjteményre hivatkoztam. Ez nem elírás. A 8086 csak 255 megszakítást képes kezelni, de sokkal több rutin kell a mûködéshez. Ezért a rutinokat csoportosították (pl.:DOS, Video-BIOS esatöbbi). Egy-egy ilyen csoport kapott egy megszakításszámot. Most az a kérdés, hogy miként választom ki, melyik rutin kell nekem. Erre ad megoldást a funkciókód, ami az adott csoporton belül azonosítja a kívánt rutint. A funkciókódot általában (gyakorlatilag mindig) az AH regiszterbe kell tölteni. Ezt a MOV utasítással tehetjük meg.
MOV
utasítás:
Használat: MOV cél, forrás
Ez betölti a forrás értékét vagy az általa meghatározott byteot a célba.
A cél és a forrás a következõk lehetnek. Itt a legtöbb cél-forrás kombinációt leírom. Ami kimarad azt majd késõbb mesélem el.
Cél |
Forrás |
Példa |
regiszter |
regiszter |
MOV BX,CX |
regiszter |
számérték |
MOV DI,szám |
regiszter |
memória rekesz |
MOV AL,memória |
16 bites regiszter |
szegmens regiszter |
MOV DX,DS |
szegmens regiszter |
16 bites regiszter |
MOV ES,AX |
szegmens regiszter |
16 bites memória rekesz |
MOV ES,memória |
memória rekesz |
számérték |
MOV memória,szám |
memória rekesz |
regiszter |
MOV memória,SI |
memória rekesz |
szegmens regiszter |
MOV memória,SS |
Azt hiszem mindent leírtam. A regiszter bármelyik regiszter lehet, a 16 bites regiszter jelzés viszont kizárja a félregisztereket (AL,Bl...). A 16 bites memóriarekesz pedig azt jelenti, hogy az assembler által 8 bitesnek értelmezett memóriaegységre nem tölthetünk be 16 bites értéket, csak ha külön jelezzük a fordítónak (az assembly forráskód szerkezetét majd késõbb).
Ezek után már meg tudjuk hívni bármelyik DOS funkciót. Teszem azt fejezzük be a programot. Ehhez a 4Ch funkciót kell meghívni a DOS funkciók közül.
MOV AH,4Ch
INT 21h
És már ki is lépett a programunk. Mielõtt bárki le akarná fordittatni most szólok, hogy nem fog tetszeni a fordítónak. Majd nemsokára kiderül miért.
CÍMZÉSI MÓDOK |
Ahhoz, hogy el tudd érni a memóriában tárolt adataidat, meg kell tudni címezni az adott részt. Elég sok címzési mód van, de egyelõre csak néhányat mondok el, a többit majd ha szükség lesz rá. Amiket most leírok egyszerûbb dolgokra elegendõek.
- Relatív címzés: Itt egyszerûen benne van az utasításban a kívánt memóriarész offszetje (ezért relatív). Ezt assembly nyelven így jelöljük: MOV [ezt_toltsd_be],AX. Itt az ezt_toltsd_be helyére bekerül az aktuális DS-hez viszonyított offszetje.
- Regiszter indirekt címzés: Egy regiszter tartalmát tekinti úgy a fordító, hogy az a megcímzendõ byte vagy szó offszetje. Példa: MOV [AX],1234h. Legyen AX=111h. Ekkor az utasítás ugyanaz, mint a MOV [111h],1234h. Csak így nem olyan rugalmas, mert mindig ugyanazt címzi, míg az indirektnél futás közben is változtathatom (persze önmódosító kóddal is, de még nem aktuális)
Most ennyit gondolok fontosnak, de akit a többi is érdekel, az a cikk végén a könyvekbõl utánanézhet. Nagyon fontos még az alapértelmezett szegmensregiszter. Ez azt jelenti, hogy minden címzési módhoz van egy szegmensregiszter, amivel a proci kiszámolja a valódi címet. Lássuk ezeket:
- Relatív címzésnél a DS az alapértelmezett, ehhez viszonyulnak az offszetértékek
- Regiszter indirekt címzésnél általában a DS az alap, de ha BP vagy SP a regiszter akkor a proci SS-t nézi
Persze kissé kényelmetlen lenne, ha csak ezeket a szegmensregisztereket lehetne használni, meg akkor mi értelme lenne a többinek. Ezért van egy un. szegmensfelülíró prefix. Ez mondja meg a procinak, hogy az alap helyett melyiket használja. Használni is egyszerû: MOV es:[kender],1234h és máris az ES-hez viszonyítva címez a proci. Ilyen felülíró lehet CS, ES, SS de ha valakinek olyanja van a DS-t is kiteheti, nem baj.
PORTOK |
A portok segítségével lehet valamilyen hardver elem részeihez hozzájutni, onnan adatot olvasni vagy oda adatot írni. Portokon keresztül vezérelhetõ a videokártya, a billentyûzet, a hangkártya, az I/O kártya, a CMOS és még sok egyéb dolog. Amit memóriába írással/olvasással nem lehet elérni, azt a portok írásával/olvasásával tesszük meg. A 8086 összesen 65536 portot tud kezelni. Persze néhány dolognál kissé egyszerûbbé tették a dolgot és nem kell a portokkal bajlódni. Ilyen pl a videokártya, amit a 10h megszakítási rutin gyûjteménnyel érhetünk el.
Persze ezek a könnyítések
mindig csak egy határig azok. Ha a feladat bonyolult vagy nagy sebességre
kell írni, akkor a BIOS körülményes lehet. Jó
példa erre egy egyszerû 640x480x256-os kép kirajzolása.
BIOSszal qrva lassú, de ha te magad írod meg a rutint minden nyûgöt
vállalva akkor gyors lesz. Példul nézd meg a KENDERMAGot.
Ha a videokártya választáskor megtaláltad a saját
kártyádat és azt választottad, akkor elég
gyors a kép kirajzolása (ne a menüt figyeld hanem egy igazi
kép kirajzolását). Viszont a BIOS-t választva (ha
az újság ismeri a kártyád, akkor is választhatod)
iszonytatóan lassú és ez kivételesen nem a programozó
(szándékosan nem coder) hibája. Majd ha olyan dolgot kell
megoldani, akkor itt is bemutatom a portok programozását. Egyelõre
elég annyit tudni, hogy az IN és OUT utasításokkal
lehet elérni ezeket.
IN
utasítás:
Használat: IN akkumulátor,port
Ez egy byteot vagy szót tölt be az adott portról az akkuba. Ha nem emlékeznél az akkumulátor az AX regisztert jelöli (meg a részeit is). Itt most csak az AX vagy AL használható. A portot kétféle módon lehet megadni. Vagy közvetlen értékkel vagy a DX-ben tárolt számmal.
Példa: IN
AL,60h ; Lenyomott billentyû
scankódjának olvasása
MOV
DX,3DAh ; Portszám DX-be, VGA státuszregiszter
IN
AL,DX ; DX által
adott portról adat olvasása
Ha közvetlen értékkel adjuk meg a portot, akkor csak az elsõ 256-ot lehet elérni, mivel az érték egybyteos lehet.
OUT
utasítás:
Használat: OUT port,akkumulátor
Egy byteot vagy szót ír ki a megadott portra. A kiírandó értéknek az akkuban kell lennie (AX vagy AL). A port itt is úgy adható meg, mint az IN utasításnál. Lényegében az IN utasítás fordítottja, így közvetlen értékkel csak az elsõ 256 port érhetõ el.
Példa: OUT
70h,al ; AL által meghatározott CMOS regiszter kiválasztása
MOV
DX,3D4h ; A VGA kártya CRTC regisztereibõl a
MOV
AL,00h ; 00h sorszámút választjuk ki a 3D4h
OUT
DX,AL ; portra való írással
Szoktak két egymás melletti portot úgy definiálni, hogy az elsõre kiírt érték a hardver elem egy regiszterét választja ki, aminek tartalmát a második porton keresztül lehet olvasni vagy írni. Ilyen megoldás van a CMOS elérésénél vagy a videokártyákon. Ilyenkor az elsõ port általában nem olvasható.
Azt hiszem ennyi elég is volt a portokról, jöjjön a forráskód.
Lucifer of ZeroBit