PDA

Prikaži potpunu verziju : Asembler 2


rile
17.7.2007, 20:54
U temi "Asembler 2" će biti reči o prelasku u zaštićeni režim rada. Program će imati neuobičajenu formu u kojoj će biti i koda namenjenog radu u 32-bitnom režimu i u 16-bitnom režimu. Podrazumevaće se da je program u .COM formatu i da se startuje pod DOS-om. Program neće raditi u Window-s dos prozoru, već se mora koristiti zasebna mašina koja je podignuta pod DOS-om.
Da bi listing programa koji demonstrira prelazak u zaštićeni režim bio upotrebljiv, neophodno je najpre navesti alate koje sam koristio kako bih olakšao onima koji žele da i sami probaju. Zato ću prvo dati alate koje sam koristio, zatim će slediti razjašnjenje stvari koje će (verovatno) biti zbunjujuće u primeru da bih na kraju dao i najavljeni primer.
Svi dobronamerni komentari, pojašnjenja, novi primeri su dobrodošli.

NAPOMENA: forma foruma je sjajna jer daje mogućnost svakom čitaocu da odmah da povratnu informaciju, postavi pitanje itd. Međutim sa druge strane forma foruma ume da učini nepreglednim glavnu temu. Zato ću pokušati da "organizujem" ovu temu (Asembler2) tako što ću da za svaki podnaslov koji sam naveo odgovoriti "samome sebi" na ovu poruku. Tako ću započeti 3 pod-teme na prvom nivou. Pitanja i komentari za svaku ponaosob mogu da idu direktno kao odgovor na odgovarajuću pod-temu... ovo doduše nikada nisam probao, pa ako napravim haos umesto organizacije, izvinjavam se :-)
NAPOMENA2: "Hibridni mod" pregleda ove poruke daje hijerarhiju o kojoj pricam (probao sam i sa malo maste moze da se kaze da je ok.).
Napomena3: kompletan kod i tekst sam prilozio ovde, za one koji nece da citaju poglavlja iz nekoliko delova.

Sadržaj:
2.1 Alati - pregled softverskih alata koji olakšavaju rad
2.2 16-bitni i 32-bitni programi
2.3 Zaštićeni režim - prelazak u zaštićeni režim i nazad.
2.4 literatura - veze na korisne tekstove.

rile
17.7.2007, 20:55
2.1. Alati
Pored asemblera koji su navedeni u temi "Asembler 1", potreban nam je i PC na kome ćemo da testiramo programe koje napravimo. Moguće je koristiti fizički PC (neku staru 386-ticu) ali je svakako daleko brže i lakše koristiti virtuelnu varijantu.
Srećom, danas na raspoloženju stoje razni alati ovog tipa. Ja ću navesti samo nekoliko (koje ja koristim) ali lista nije konačna i sigurno je moguće koristiti druge:
1. Virtual PC 2007, Microsoft, besplatan. Može se preuzeti odavde: http://www.microsoft.com/downloads/details.aspx?FamilyId=04D26402-3199-48A3-AFA2-2DC0B40A73B6&displaylang=en
2. Bochs - odlična aplikacija, za razliku od virtuelnih PC-jeva (Virtual PC, VmWare, i slično) Bochs emulira rad PC-a. Neverovatno korisna alatka, naročito u fazi učenja i inicijalnih testova. Može se preuzeti odavde: http://bochs.sourceforge.net/cgi-bin/topper.pl?name=See+All+Releases&url=http://sourceforge.net/project/showfiles.phpqmrkgroup_ideq12580, ako ovo slučajno ne radi, idite na početnu stranu Bochs projekta pa odande navigacijom idite na "Release page": http://bochs.sourceforge.net/getcurrent.html
3. Virtuelni flopi drajv, veoma koristan i može da se koristi i sa 1) i sa 2). Može se preuzeti odavde: http://chitchat.at.infoseek.co.jp/vmware/vfd.html#download.
Po instalaciji virtuelnog flopi drajva (VFD), otvorite dijalog "vfdwin.exe" i instalirajte drajver, zatim izaberite slovo (ako nemate fizički drajv, izaberite A:. Ako ga imate, izaberite neko slobodno slovo drajva i upamtite ga, trebaće vam za podešavanje Bochs-a i/ili VPC2007). Pod windows XP-jem, možete da formatirate i izaberete "make disk bootable" što će instalirati neku verziju DOS-a. Alternativno, možete koristiti sliku flopi diska iz priloga ovoj poruci.

rile
17.7.2007, 20:57
2.2 16-bitni i 32-bitni programi (deo 1)
U ovom delu navodim generalne napomene koje se odnose na programiranje na 386 i novijim procesorima (dakle na 32-bitne procesore). Za primere koje ću navesti u ovom delu koristim nasm za windows (nasmw.exe) i nasm-ov disasembler (ndisasmw.exe).

Režimi rada procesora 386
Procesori 386 i noviji imaju 16-bitni režim rada koji je veoma blizak načinu rada procesora 8086 i naziva se "realni režim rada", sa bitnom razlikom da je izvršavanje 32-bitnih instrukcija moguće. Osim ovog, imaju i 32-bitni režim rada (naziva se "zaštićeni režim rada" odnosno na engleskom "protected mode"). I u ovom režimu mogu da izvršavaju i 32-bitne i 16-bitne instrukcije (dakle isto kao i u realnom). Međutim, razlike su ogromne. U ovom delu ću pričati samo o razlikama vezanim za širinu mašinske reči, a u nastavcima će biti više reči o ostalim razlikama.
Većina današnjih opšte-poznatih operativnih sistema radi u zaštićenom režimu rada, ali DOS, na primer, radi u 16-bitnom režimu.
Napomena: Na Internetu možete naći da osim ova dva načina postoji i "nad-realni" režim rada u kome se procesor prevede u zaštićeni režim rada, određeni parametri se promene, a zatim vrati u realni režim, ali ovog puta limiti više nisu 64k već ofseti mogu da budu i veći (do 32 bita) što efektivno stavlja na raspolaganje kompletnu fizičku memoriju 16-bitnom programu. Ovaj režim neću da opisujem.
Šta znači 16-bitni, a šta 32-bitni program?
Terminologija može malo da zbuni jer asembler može da generiše i 16-bitne i 32-bitne instrukcije u jednom te istom programu.
Jednostavna definicija 16-bitnog programa bi bila "program koji je namenjen za izvršavanje u realnom režimu rada procesora" dok bi 32-bitni program bio "program koji je namenjen radu u zaštićenom režimu rada".
Komplikovanije objašnjenje sledi.
Važno je odmah razlučiti da termin "16-bitni program" ne znači da 32-bitne instrukcije nisu dostupne. Na 386 i novijim procesorima, one se mogu normalno izvršavati. Isto tako, "32-bitni program" može sasvim normalno da sadrži 16-bitne instrukcije. Tako, na primer, ako se poigrate sa primerima datim u temi "Asembler 1" možete da umetnete 32-bitne instrukcije (pod uslovom, naravno, da generisani program izvršavate na 386 ili novijem procesoru) i program će raditi kako treba.

rile
17.7.2007, 20:58
16-bitni i 32-bitni programi (deo 2)

Dužina mašinske reči i širina operanda

Procesori 386 i noviji (pravi 32-bitni procesori) imaju dužinu reči koja zavisi od režima u kome se nalaze: u realnom režimu podrazumevana dužina reči je 16 bita, dok je u zaštićenom 32 bita. Ova podrazumevana dužina reči, uslovljena režimom rada, se može priveremeno promeniti specijalnim prefiksima koje modifikuju narednu instrukciju (detaljnije dalje u tekstu).

Za početak, razmatramo izvršavanje 16-bitne instrukcije najpre u 16-bitnom programu, a zatim u 32-bitnom programu.
Razmatramo dve jednostavne linije koda:
određena konstanta (u ovom slučaju B800h) se učitava u akumulator a zatim u neki od segmentnih registara.
Izvorni kod za NASM:

[bits 16] ; generiši 16-bitni program
mov ax, 0B800h
mov es, ax

Kada se asemblira, mašinski kod (u heksadecimalnom brojevnom sistemu) izgleda ovako:

B800B8 ( mov ax,0xb800 ) Prvi B8 je instrukcija, drugi je deo konstante
8EC0 ( mov es,ax )

(u zagradi su dati mnemonici, radi lakšeg praćenja).


Šta se dešava kada procesor "stigne" do prvog bajta u gornjem nizu: procesor učitava vrednost B8h, prepoznaje da se radi o instrukciji i zatim učitava operand. I ovde, kod operanda dolazi do ključne razlike: u 16-bitnom režimu, procesor očekuje da iza B8 nailazi tačno 2 bajta operanda (u 16-bitnom režimu, dužina mašinske reči je 16 bita). Procesor učitava sledeća dva bajta i smešta ih u akumulator (bajtovi 00B8h su konstanta B800h, intelovi procesori tako ređaju konstante u memoriji, od bajta najmanje težine ka većim). Zatim nastavlja sa izvršavanjem 8EC0h što znači "stavi vrednost iz ax akumulatora u segmentni registar es".

Evo sada istih instrukcija ali u 32-bitnom programu:

[bits 32] ; generiši 32-bitni program
mov ax, 0B800h
mov es, ax

(izvorni kod se razlikuje samo u direktivi asembleru "bits 32" koja mu kaže da podrazumeva da će se program izvršavati u 32-bitnom režimu rada).
Mašinski kod programa izgleda ovako:

66B800B8 ( mov ax,0xb800 )
8EC0 ( mov es,ax )

Primećujemo "ekstra" bajt 66h. Ovo je prefiks koji govori procesoru da sledeća instrukcija treba da se interpretira "u onom drugom" režimu. Znači ako smo u 16-bitnom, nailazi 32-bitna instrukcija, a ako smo u 32-bitnom, nailazi 16-bitna instrukcija. U našem primeru, direktivom "bits 32" smo rekli asembleru da će se kod izvršavati u 32-bitnom režimu. Zapravo prefiks samo modifikuje instrukciju koja je i dalje B8h - "učitaj u akumulator" ali su širina operanda i odredište različiti. U 32-bitnom režimu rada, podrazumevana destinacija instrukcije B8h je 32-bitna (odnosno eax) kao i operand. Kako smo u ove dve linije gore specificirali ax, asembler je umetnuo prefiks 66 čime je rekao procesoru da "iako si u 32-bitnom režimu, učitaj samo sledeća dva bajta u donja dva bajta eax registra".

rile
17.7.2007, 20:59
16-bitni i 32-bitni programi (deo 3)

Radi kompletnosti, evo kako izgleda 32-bitna instrukcija u 16-bitnom programu:

[bits 16]
mov eax, 0B800h
mov es, ax

mašinski kod:

66B800B80000 ( mov eax,0xb800 )
8EC0 ( mov es,ax )

Ovde prefiks 66"h kaže procesoru da operand sledeće instrukcije treba tretirati kao 32 bita. Operand B800h je dopunjen do širine od 32 bita (poslednje 4 nule)
Iste dve linije koda u 32-bitnom programu:

[bits 32]
mov eax, 0B800h
mov es, ax

i odgovarajući mašinski kod:

B800B80000 ( mov eax,0xb800 )
8EC0 ( mov es,ax )

Primećujemo da asembler nije ubacio nikakav prefiks jer je program predviđen za izvršavanje u 32-bitnom režimu rada. I ovde je asembler "dopunio" širinu operanda do 4 bajta umetanjem nula u dva bajta veće težine.

Šta bi se desilo ako bi ipak pokušali da izvršimo kod predviđen za rad u jednom režimu, u onom drugom?
Lako možemo da vidimo da bi pokušaj izvšavanja 16-bitnog programa u 32-bitnom režimu rezultirao greškama (i obrnuto). Recimo da izvršavamo prvi primer, odnosno 16-bitni program, u 32-bitnom režimu:

B800B8 ( mov ax,0xb800 )
8EC0 ( mov es,ax ) ovde je greška

Procesor je u 32-bitnom režimu, čita B8, znači "učitaj u akumulator" odnosno u eax i očekuje da su 4 bajta koja slede operand. Čita zato niz bajtova "00B88EC0" odnosno broj C08EB800. Instrukcija "mov es, ax" biva "pojedena" kao operand prethodne insturkcije. Brzi krah programa je neizbežan.
Širina adrese
Slično operandima, u zavisnosti od režima rada operand koji se odnosi na adresu ima različitu dužinu. Neću ulaziti u detaljnu analizu jer bi bila veoma slična analizi širine operanda osim što u tom slučaju prefiks koji modifikuje instrukciju i kaže joj da je adresa "one druge dužine" iznosi 67h (za razliku od 66h koji modifikuje širinu operanda).
Zaključak
Kada pišemo asembler program u kome se javlja i 16-bitni i 32-bitni kod najbolje je razmišljati u terminima predviđenog načina rada procesora, a ne sekcije unutar izvornog koda. Instrukcije, kao što smo videli, se mogu ravnopravno koristiti (i 16-bitne i 32-bitne) u obe vrste programa bez problema ali sam asembler može da nam iskomplikuje stvari umetanjem prefiksa za modifikovanje širine operanda/adrese. Ukoliko ne razumemo tačno šta se dešava "iza scene", teško ćemo uspeti da napravimo program koji radi :-)

rile
17.7.2007, 21:01
2.3 Zaštićeni režim - prelazak u zaštićeni režim i nazad (deo 1)
U poglavlju 2.2 sam namerno uprostio stvari i rekao da postoji 16-bitni režim i 32-bitni režim. Namerno sam upotrebio posebne termine uporedo sa terminima "realni režim" i "zaštićeni režim". Ovde moram da proširim objašnjenje i iskomplikujem ga (ne vidim kako drugačije opravdati ono što se dešava u kodu koji dajem ispod).
Podrazumevana širina mašinske reči i način formiranja fizičke adrese su dva različita parametra. Najčešće je u zaštićenom režimu rada podrazumevana 32-bitna širina maš. reči, ali nije obavezno. Istina je da je moguće izvršavati program u 16-bitnom režimu a biti i dalje u zaštićenom režimu, kao što je moguće izvršavati kod u 32-bitnom režimu a ne biti u zaštićenom režimu. Ovo jeste jako zbunjujuće (mene još uvek zbunjuje) ali šta da se radi.
Terminologija - "vrste" adresa
Kada pričamo o načinima rada, srećemo se sa različitim izrazima kojima označavamo adrese. Ovde pokušavam da dam neke definicije.
Logička adresa Logička adresa se sastoji od vrednosti u segmentom registru i ofseta (kada kažemo logička adresa to je uvek u kontekstu režima rada, odnosno ne možemo samo na osnovu logičke adrese da odredimo fizičku)
Fizička adresa Fizička adresa je "prava" adresa. To je adresa koja ide na adresnu magistralu procesora.
Linearna adresa Ovaj termin se koristi u takozvanom "ravnom" memorijskom modelu. U ovom modelu, aplikaciji je na raspolaganju "ceo" adresabilni prostor procesora (za 386 i novije, to je 4Gb, za 8086 to je 1Mb a za 80286 to je 16Mb).
Fizička adresa
Da bi procesor zaista izvršio bilo kavo premeštanje podatka iz ili u memoriju, na neki način mora da izračuna fizičku adresu. Ovo se dešava unutar procesora (implementirano u hardveru) a kako konkretno to radi zavisi od načina rada (režima).
Bez obzira na režim, procesor najpre proverava specijalne interne registre (potpuno nevidljive i nedostupne za nas) u kojima "vidi" da li odgovarajući segmentni registar predstavlja segment adresu ili segment selektor. Ukoliko se radi o segment adresi (u "normalnom" realnom režimu rada) jednostavno množi ovu vrednost sa 16 i dodaje ofset. Ukoliko se radi o "segment selektoru" onda iz posebnog internog registra čita vrednost bazne adrese, pa ovoj dodaje ofset i tako dobija fizičku adresu. Prirodno, pitate se odakle ove vrednosti u tim "specijalnim" internim registrima? Jedini način da se ovi registri promene jeste promenom vrednosti u segmentnim registrima, uključujući i kodni segment CS. Ali sačekajte sa donošenjem zaključaka dok ne pročitate sve i ne pogledate primer.

rile
17.7.2007, 21:03
2.3 Zaštićeni režim - prelazak u zaštićeni režim i nazad (deo 2)

GDT - Globalna tabela deskriptora ("Global Descriptor Table")
Ova tabela je struktura u memoriji, možemo reći niz, od 8-bajtnih struktura podataka koje se zovu "segment deskriptori". Poređani u niz čine globalnu tabelu deskriptora.
Evo kako izgleda jedan segment deskriptor (logički):
Bazna adresa (4 bajta)
Limit (4 bajta)
Specijalni bitovi (11 bitova) koji odredjuju:
- vrstu segmena (code/data),
- da li je dozvoljeno pisanje u segment ili samo čitanje,
- da li je fizički prisutan (koristi se za implementaciju takozvanog "paging" memorije),
- neophodni nivo privilegija,
- DUŽINA maš. reči (da li je 16-bitni ili 32-bitni, o ovome je bilo reči).
Fizički, struktura je prilično čudna (vidi primer).

Kada procesor učitava GDT i posledice ovakvog ponašanja
Ova struktura se u realnom režimu ne koristi i ne mora da postoji. Za zaštićeni režim mora da se fizički nalazi u memoriji ali je procesor čita samo pod određenim uslovima: isključivo u situaciji kada smo iz realnog prešli u zaštićeni režim (setovali smo nulti bit registra cr0, vidi primer) i promenimo vrednost segmentnog registra.
Obrnuto, ako smo isključili zaštićeni režim rada (resetovali bit 0 u registru cr0) učitani segment desktiptori nastavljaju da važe sve dok ne učitamo novu vrednost u segmentni registar. Ali sada će nova vrednost biti interpretirana kao segment-adresa i GDT više ne mora ni da postoji.
Ovo moram da pojasnim daljim primerima:
Recimo da smo u "normalnom" realnom režimu. Procesor interno ima učitan segment deskriptor (ovaj deskritpor nije učitan iz memorije nego je postavljen internom inicijalizacijom procesora posle resetovanja).
Setujemo bit 0 u cr0, čime smo rekli procesoru "kada se sledeći put promeni neki segmentni registar, interpretiraj novu vrednost kao segment selektor a ostale informacije učitaj iz memorije koristeći GDT čiji se početak nalazi na fizičkoj adresi specificiranoj u registru GDTR.
Ponavljam, čitanje GDT se dešava samo kada je bit 0 cr0 postavljen na 1 i kada se promeni vrednost u segmentnom registru.
Sada resetujemo bit 0 u cr0. Protekcija više ne radi, ali procesor ne radi ništa po pitanju učitanih parametara segmenta sve dok ne promenimo vrednost segmentnog registra. Znači, radimo u 32-bitnom nezaštićenom modu rada (nezaštićen jer ne proverava limit, pravo pristupa i vrstu memorije, ali i dalje koristi baznu adresu za računanje fizičke adrese).
Još jedna interesanta činjenica: čak i kada izvršimo "jmp far" i time forsiramo promenu vrednosti u CS, vrednosti u DS i ostalim segmentnim registrima se i dalje ponašaju po poslednjem pravilu sve dok im se vrednost eksplicitno ne promeni.


Uh. Jezivo. Nadam se da će primer rasvetliti stvari.

rile
17.7.2007, 21:11
2.3 Zaštićeni režim - prelazak u zaštićeni režim i nazad (deo 3)
Konačno primer:
U prilogu: asm2.txt

rile
17.7.2007, 21:12
2.4 Literatura - veze na korisne tekstove
U ovom poglavlju navodim neke od izvora informacija koje sam koristio. Na žalost, skoro svi su na engleskom jeziku. Nisu poređani po važnosti, nego onim redom kako su "ispali" u mojim "bookmarks".

Literatura
1. Tekst iz časopisa Embedded Systems Programming iz avgusta 1991
386 Protected Mode (part 1) (http://www.avocetsystems.com/company/articles/magazine/aprot1.htm)
386 Protected Mode (part 2) (http://www.avocetsystems.com/company/articles/magazine/aprot2.htm)
2. Načini adresiranja
80x86 Addressing Modes (http://www.dcc.unicamp.br/~celio/mc404s2-03/addr_modes/intel_addr.html)
3. Solidan pregled osnova na najnižem nivou (ko hoće da razume vezu između mašinskog koda i mnemonika instrukcija)
80X86 Programming (http://www.du.edu/~etuttle/math/8086.htm)
4. Tekst koji priča o nekim stvarima o kojima je diskutovano u ovoj temi (opširnije i verovatno bolje objašnjeno)
Embedded X86 Programming: Protected Mode (http://www.embedded.com/98/9804fe4.htm)
5. Tekst koji objašnjava različite režime rada procesora (sažeti pregled režima)
Introduction to Protected Mode Programming (http://www.monstersoft.com/tutorial1/PM_intro.html)
6. Još jedno objašnjenje zaštićenog režima rada
General description of Real Mode, Protected and V86 Mode (http://www.deinmeister.de/x86modes.htm)
7. Pojašnjenje segmentnih registara i njihovog značenja u različitim režimima rada procesora
http://my.execpc.com/~geezer/johnfine/segments.htm
8. Prelazak u zaštićeni režim rada
Chapter 16 Starting Protected Mode (http://courses.ece.uiuc.edu/ece390/books/labmanual/start-pmode.html)