Navigation


RSS: tutorialok Jelenleg 6 vendég és 0 tag van az oldalon.

8086 Assembly 1.

szerző: Mr_Steve
dátum: 2008-05-08
Kategóriák:
  Assembly/80x86

Mielőtt belevágnánk a lecsóba leírom, hogy mire lesz szükségünk:

-TASM avagy Turbo Assembler -> Google -> tasm download, első oldalon bármelyik jó
-TLINK, általában ott van a TASM mellett
-Valamilyen szövegszerkesztő (én Notepad+ -t használok)
-Windows operációs rendszer (lehetőleg XP, Vistához DosBox kell (ingyenes))
-Alapvető DOS parancsok ismerete mivel parancssorban dolgozunk
-Kettes számrendszer ismerete
-Tizenhatos számrendszer ismerete
-Byte, bit és társaik ismerete
-Valamilyen magasszintű programnyelv (Pascal, C, stb.) legalább alapszintű ismerete

Ebben a cikksorozatban 16 biten, valós módban az Intel 8086 -os processzorának utasításkészletével fogunk programozni.

Következzék egy kis történelmi visszatekintés:
A 8086 és 8088 processzorok diadalmenete 1983 -ban kezdődött, amikor az IBM úgy döntött, hogy rájuk fog PC -t építeni. Erre a pozícióra eredetileg hárman pályáztak, az Intelen kívűl a Motorola 68000 és egy házon belül épített processzor volt még versenyben, végül az Intel procik kedvező ára és alternatív beszerezhetősége (AMD) döntötte el a kérdést. A többi már történelem...

Most megismerkedünk egy kicsit a számítógép felépítésével (csak nagy vonalakban):
Valójában két dolog fontos nekünk, méghozzá a processzor és a memória. Előbbi hajtja végre azokat az utasításokat, amiket a memóriában tárolunk.
A végrehajtást egy órával ütemezi be magának, minden utasításnak megvan az adott "ciklusigénye" (egy 1GHz -es proci kb. egy milliárd ciklust hajt végre másodpercenként).
A CPU gépi kódot eszik, ami egy igen gusztustalan valami, ezért néhány évtizeddel ezelőtt a programozó nénik és bácsik úgy döntöttek, hogy megkönnyítik az utókor helyzetét és rövid (két-három-négy betűs) utasításokkal (ún. mnemonic -okkal) váltották ki a gépi kódú parancsokat. Ez még mindig nem túl szép, de emészthető. Az assembler - ez jelen esetben a TASM - ezeket a mnemonic -okat alakítja gépi kóddá.
A processzornak vannak még regiszterei is, ezek aranyos kis dobozok amelyek segítségével a processzor a műveleteket végzi. Ezeknek az elérése piszkosul gyors, gyorsabb mint a memóriáé.
A 8086 procinak 14+1 regisztere van, a legtöbbnek van speciális funkciója, ezekkel majd nemsokára megismerkedünk. Elrettentésképpen egy rövid felsorolás:
AX, BX, CX, DX, ES, DS, SP, BP, SI, DI, SS, CS, SP, IP és a FLAG
A 8086 minden regisztere 16 bitet tud tárolni. Van azonban néhány regiszter, ami további két 8 bites egységre (alsó és felső rész) bontható (később). Mint mondottam vannak spec regiszterek, de van néhány ami általános célú (AX, BX, CX, DX - általában ők). Namármost a spec regiszterek egy részéhez nemigen ajánlott nyúlkálni (pl.: CS, IP, DS).

Most nézzük a memóriát. A memória byte -os szervezésű, azaz egyszerre legalább egy byte -ra kell hivatkoznunk (ennek mindjárt lesz jelentősége). A mai számítógépek kétféleképpen tudjál elérni a memóriát: valós és védett módban. Mi az előbbit fogjuk használni, ezzel mintegy szimulálva a 8086 processzort. Valós módban 1MB memóriát tudunk használni (ezzel kapcsoladban terjeng egy városi legenda, miszerint az Intel mérnökei nem gondoltak arra, hogy valaha is szükség lesz többre, valójában gondoltak rá, de erre volt keret), viszont van egy kis bökkenő. Valós módban a memóriacímek 16 bitesek, mivel egy regiszterben ennyi fér el, így viszont csak 65536 féle címünk lehet, ami 64 kilobyte -nak felel meg. Akkor, hogy a fenébe lesz ebből egy megabyte? Úgy, hogy a memóriát szegmensekre osztották. A szegmensek 16 byte -onként követik egymást a memóriában és 64kb hosszúak (ez természetesen azzal jár, hogy az egyes szegmensek közt átfedés van). Na, most gondolkodjunk egy kicsit. Egy megabyte osztva 16 byte -al az 65536 byte, azaz van 65536 szegmensünk. Ahhoz, hogy elérjük a szegmenseken belül lévő adatokat Intelék bevezettek egy második ún. offszet címet, ami azt mutatja meg, hogy egy adott szegmensen belül hol vagyunk. Tehát egy memóriacím így néz ki szegmens:offszet, ahol mindkettő egy 16 bites szám. Hogy még mindig ne legyünk boldogok, nekünk egy konkrét 20 bites szám kell, mivel csak ezt tudjuk átpasszintani a címbuszon (2^20 == 1MB), csakhogy a szegmens:offszet módszer 32 bitet eszik. Most jön a trükk!
A szegmens címet eltoljuk 4 bittel balra (2^4 == 16 itt jön be a 16 byte -os szegmenshatár és az elején említett byte -os szerveződés) és hozzáadjuk az offszetet. Ezt a kedves kis műveletet szerencsére a processzor elintézi, mi
szegmensekkel fogunk dolgozni, de mindenképpen fontos a szegmentált memóriakezelés megértése, ezért csak akkor menjetek tovább, ha értitek.

Van még valami, amire szükségünk lesz, ez az ún. interrupt vagy megszakítás, ezen belül is a szoftveres fajta. Ezek tulajdonképpen kis programok, amiket jelen esetben a BIOS és a DOS szolgáltat nekünk.

Eljött az ideje, hogy írjunk egy programot. DOS alatt kétféle végrehajtható állomány létezik, EXE és COM. A kettő között a lényegi különbséget a méret képezi, egy eXE bármekkora lehet, de a COM -nak be kell férnie egy szegmensbe, azaz maximum 64 kbyte hosszú lehet. Továbbá a COM programok karcsúbbak is egy kicsit, mivel az EXE -khez jár egy fejléc, ami a futtató környezetnek szolgál információkkal.

Mi most egy COM progit fogunk írni, mert az rövidebb :). A feladat a klasszikus "Hello World!" lesz.
Minden assemblernek különböző szintaktikája van - de a parancsok ugyanazok - így a TASM esetében is be kell tartani néhány formai követelményt. Egy COM így néz ki:
  1. "Program" Segment
  2. assume cs:"Program", ds:"Program", ss:"Program"
  3.  
  4. org 100h
  5.  
  6. "Start:"
  7.  
  8. ret
  9.  
  10. "Program" Ends
  11. End "Start"
  12.  

Az idézőjeles részek tetszőlegesen lecserélhetőek, de ami itt egyforma annak mindig egyformának kell lennie.
A "Segment" címke kijelöli a szegmensünk kezdetét, ennek az "Ends" címkével vetünk véget.
Az "assume" segítségével beállítjuk három speciális regiszternek (cs == code segment, ds == data segment,
ss == stack segment), ún. szegmensregisztereknek az értékét. Itt mindhárom ugyanoda mutat (egy szegmensben kell elférnünk), exe -nél ez máshogy lesz.
Az "org 100h" a programunk kezdőcímét jelöli ki, a 100h alatti címen a programunkra vonatkozó paraméterek vannak, ezért célszerű innen kezdeni.
A program végén az "End" lezárja a programot az utána következő címke pedig azt mutatja meg, hohy hol kezdődik a program. Itt fontos figyelni arra, hogy nem kell kettőspont, míg az eredeti címkénél igen.
A "ret" teljes mértékben megegyezik a magasszintű nyelvek return utasításával, azaz visszadja a vezérlést az őt hívónak. Vagyis most kilépünk.

Most jöjjön a "Hello World!", utána magyarázat:
  1. Program Segment
  2. assume cs:Program, ds:Program, ss:Program
  3.                
  4. org 100h
  5.  
  6. Start:
  7.         mov dx, offset msg
  8.         mov ah, 09h
  9.         int 21h
  10.        
  11.         xor ax, ax
  12.         int 16h
  13.        
  14.         RET
  15.        
  16. msg: db 'Hello World!$'
  17.  
  18. Program Ends
  19.  
  20. End Start
  21.  

Fordítani a követekzőképpen kell:


tasm.exe filenev.asm
tlink.exe /t filenev.obj


Ezután egy '.com' kiterjesztésű fájlnak kell keletkeznie.
A mov utasítással adatokat mozgathatunk, bal oldalt van a cél, jobbra a forrás. Egy kicsit előre ugrok, mert így érthetőbb. Az "int 21h" sorral meghívjuk a DOS kernelt, hogy csináljon ezt-azt. Hogy pontosan mit, azt az ax regiszter alsó felébe (ah) helyezett "09h" hexadecimális számmal jelezzük: jelen esetben kiírunk egy szöveget, aminek az offszetje a dx regiszterben van. Az "msg" egy címke, ami egy byte -nyi memóriahelyre mutat, itt kezdődik a stringünk, amit a '$' karakterrel zárunk le.

A "xor" utasítás egy kizáró-vagy műveletet hajt végre az operandusain, ha ugyanazt a regisztert adjuk meg mindkét helyen akkor kinullázza (méghozzá gyorsabban mint
a mov ax, 0). Ez azért kell, mert a következő sorban a BIOS billentyűzetkezelő megszakítását hívjuk, ami vár egy leütést, ha ah == 0.

Ennyi elég is lesz mára. Jó szórakozást!