Računari 78

Zablude o 80486

80486 je među nama već dve godine (80386 dosta duže), a ipak se o njemu u suštini jako malo zna. Pominjemo ga, naravno, kao centar raznih moćnih PC platformi, hvalimo njegovu brzinu i fleksibilnost, "mučimo" ga benčmark testovima, ali ga u svim tim prilikama tretiramo kao "malo" brži 8086/8088. O njegovim pravim 32-bitnim mogućnostima zna se jako malo, a i i dosta toga što se "zna" baš i nije tačno. Ovaj napis posvećujemo nekima od tih zabluda.

Dejan Ristanović

Na sve tvrdnje koje ćemo u sledećim redovima "demantovati" smo negde naišli - većinu smo pročitali u domaćim ili stranim računarskim publikacijama, nešto se čulo u razgovorima (u kojima su tvrdnju svi prisutni prihvatali kao neospornu), u ponešto smo i sami odnekud bili ubeđeni... Pa ipak, čak i površan pregled dokumentacije mikroprocesora 80386 i 80486 pokazaće da se radi o zabludama. Napomenimo još da svaka "zabluda" ne mora uvek biti sasvim pogrešna - neke od stvari koje slede naprosto nisu dovoljno precizno iskazane, a ima i situacija da je tvrdnja gotovo tačna, ali ne iz razloga koji se navode (kao kada, rešavajući neki zadatak, pogrešite dva puta, greške se potru pa rezultat ispadne tačan). No, da krenemo.

1. Mikroprocesori 80386 i 80486 su sa softverske strane identični. Zapravo, ne postoji način da program sazna da li se izvršava na 80386 ili na 80486

Ovu frazu sam toliko lako ispisao da se prosto bojim da mi ovo nije prvi put. Nešto slično je pisalo u BYTE-u pre nekoliko godina, a pominje se i na BIX-u s vremena na vreme. Verovatno i na mnogim drugim mestima. A sa istinom nema mnogo veze - između 80386 i 80486 postoje itekako značajne softverske razlike, naravno u smeru uobičajenom za Intelove procesore: 80486 u sebi "sadrži" kompletan 80386 (vertikalna kompatibilnost), ali ima i neke nove elemente. Čak tako velike kao što su registri: 80486, na primer, uvodi registre TR3, TR4 i TR5. Ova tri konkretna registra omogućavaju dijagnostičkom softveru (npr. kodu na početku BIOS-a) da proveri internu keš memoriju pristupajući TLB-u (translation lookaside buffer). Osim toga, 80486 ima i jedan novi fleg zvani AC (aligmnment check) čije postavljanje omogućava dobro pisanim operativnim sistemima (ima li takvih?) i kompajlerima da znatno ubrzaju izvršni kod time što će "zagarantovati" mikroprocesoru da svaki podatak koji će se čitati ili pisati počiva na adresi deljivoj sa 4. Ukoliko se docnije pokaže da nije tako, izvršavanje traje daleko duže nego što bi trajalo da fleg nije setovan. Ima i novih instrukcija specifičnih za 80486, premda su mnoge od njih po značaju marginalne. Da ne ostanemo bez dokaza i za ovu tvrdnju, pomenućemo instrukciju BSWAP.

2. Uz mikroprocesor 8086 koristi se aritmetički koprocesor 8087, uz 80286 - 80287, uz 80386 ide 80387 a uz 80486? Naravno, 80487

Dobro, dobro, nisam ni tvrdio da neko od čitalaca ovog teksta stvarno tako misli. Pa ipak, tvrdnja je više puta izrečena (recimo na našoj televiziji) pa zaslužuje da se nađe na spisku. Integralni deo mikroprocesora 80486 je jedinica za operacije za brojevima u pokretnom zarezu koja je vertikalno kompatibilna sa prethodnim Intelovim aritmetičkim koprocesorima. Ili, prostije i nepreciznije rečeno, u 80486 je već "ugrađen" 80487.

Znatno posle 80486, pojavio se 80486SX u koji ovaj koprocesor nije "ugrađen". Ploče sa 80486SX u ovom trenutku predstavljaju prilično lošu kupovinu (ako vam, recimo za DTP, treba snaga 80386/80486 ali vam ne treba koprocesor, napravićete bolji posao ako uzmete 80386 na višoj frekvenciji) ali to je već druga priča...

3. Interni aritmetički koprocesor ugrađen u 80486 se ne može isključiti

Ako je baš potrebno, može. Flegovima ET i EM u prvom kontrolnom registru (CR0) može se simulirati sistem bez koprocesora.

4. Ukoliko 80387 ne postoji, 80386 ga emulira, doduše uz logičan gubitak na performansama

Na žalost, nije tako. 80386 ne može "tek tako" da emulira 80387. Ali, setovanjem EM bita u CR0 registru (emulate math coprocessor) procesoru se nalaže da, čim naleti na instrukciju za koprocesor, generiše interapt (trap) i preda kontrolu operativnom sistemu. Tamo može biti potprogram koji realizuje željene operacije ili možda komunicira sa nekim koprocesorom nezavisnog proizvođača. Neki 80386 BIOS-i zaista sadrže takve programe.

5. 80486 ima interni keš od 8K. Taj keš se ne može isključiti.

Ne samo da se može isključiti, nego se može kontrolisati da li će taj keš biti read through, write through (za šta bi to služilo?) ili read/write. Za to su zaduženi flegovi CD i NW (Cache disable i No write-through) u CR0.

6. Nepostojeća instrukcija se na 80486 tretira kao NOP (No Operation)

Kladio bih se da je ovu tvrdnju (pročitao sam je nedavno u jednom domaćem kompjuterskom časopisu) izrekao neki bivši vlasnik Commodore 64 ili nekog drugog računara zasnovanog na mikroprocesoru 6502 firme MOS Technology. Ovaj mikroprocesor je, naime, "ludeo" kad naiđe na nepostojeću instrukciju a onda su napravljeni 65C02 i neke druge varijante 6502 koje su nepostojeće instrukcije zaista tretirale kao NOP što je bio priličan korak napred.

Na 80286/386/486 se otišlo još dalje - nailazak na nepostojeću instrukciju izaziva takozvani invalid opcode fault tj. predaje kontrolu rutini koja opslužuje INT 6. Ista stvar se dogodi čak i kada je neki od operanada neispravan ili kada korisnik pokuša da izvrši neku 32-bitnu instrukciju dok se nalazi u virtualnom 8086 modu.

7. 80486 je, kada radi u realnom modu, sasvim kompatibilan sa 8086 i 80286. Svaki program koji je radio na njima radiće i na njemu

Skoro da je tačno, ali ipak... reč svaki je malo prejaka! Kompatibilnost, na primer, "pada" baš na nepostojećim instrukcijama, i to naročito na relaciji 80286-80486. Ukratko rečeno, neki kod koji je na 8086 bio nepostojeća instrukcija i izazivao neko nedokumentovano ponašanja mikroprocesora, odnosno koji je na 80286 izazivao pomenuti invalid opcode fault, na 80386/486 može da bude sasvim legalna instrukcija. Teško da bi neki programer na ovome zasnovao neki komercijalni proizvod (možda eventualno neku zaštitu?) pa je u praksi tvrdnja 7 "dovoljno" tačna. Ipak, teorijski bi mogao da nastupi "kuršlus".

Postoji i dvadesetak drugih razlika, ali su sve one jako periferne. 80486, na primer, ne može da "podnese" instrukciju dužu od 15 bajta, dok 8086 (zahvaljujući daleko primitivnije rešenoj fazi pripreme naredbe) ne pravi pitanje oko dužine instrukcije. Instrukcija duža od 15 bajta normalno ne postoji, ali se može sintetisati korišćenjem gomile redundantnih prefiksa.

8. 80286, 80386 i 80486 se ne mogu "vratiti" iz zaštićenog u realni mod. Jedini način da se to izvede je resetovanje mikroprocesora

Uh! Ima li neko ko ne misli tako? Bez obzira na to koliko smo je puta ponovili (npr. u "Računarima"...), tvrdnja je tačna samo u slučaju 80286 - njegovi tvorci su se očito "plašili" da će se neka aplikacija prebaciti iz zaštićenog u realni mod a onda po volji vršljati memorijom. Na 80386 i 80486 je uvedena "policija" koja se o tome brine, pa se prostim setovanjem i resetovanjem PE (protect enable) flega u CR0 "šeta" između zaštićenog i realnog moda. Naravno, ukoliko proces ima privilegije da na taj fleg utiče.

9. 80386 i 80486 su prevazišli segmente - u zaštićenom modu memorija je linearna ali...

Ovo i nije toliko pogrešno koliko je neprecizno. 80386 i 80486 uvek rade sa segmentima.

10. ... kada se radi sa segmentima, onda je svaki segment ograničen na 64 K

Srećom, nije! Segment je sada ograničen na 4 gigabajta (232) što je ujedno i adresni prostor svakog 32-bitnog procesora. Dakle, koliko god da imate memorije, možete je čitavu staviti u jedan segment, pa zbog toga i rekosmo da prethodna tvrdnja nije precizna ali je u suštini tačna. Segmente ćete, dakle, koristiti kada su zaista potrebni, a to i nije tako redak slučaj - višeprogramski rad je, na primer, lakše organizovati uz segmentiranu memoriju. Ukoliko se, sa druge strane, portira neki operativni sistem koji je zasnovan na linearnoj (flat) adresnoj mapi (npr. Unix), segmenti se lako zaobiđu.

11. Kada mikroprocesor u zaštićenom modu radi sa virtualnom memorijom, segment je ograničen na 1 megabajt

Znajući prethodno, tvrdnja očito nije tačna. Pa ipak, nedavno su uspeli da me "ubede" da jeste. Dokaz "ide" otprilike ovako: kompletan rad sa virtualnom memorijom na 80386 i 80486 zasniva se na takozvanim deskriptorima segmenata - neka vrsta mape koja govori gde se u realnom adresnom prostoru nalazi određena virtualna stranica memorije. Prva reč (16 bita) deskriptora označava se kao limit - tu piše koliko je segment veliki kako aplikacija ne bi, usled greške programera, "bombardovala" tuđi adresni prostor. Tako je bilo na 80286, gde je segment bio ograničen na 216=64K. Kod 80386 i 80486 polje limit je "dobilo" još i prva četiri bita četvrte reči deskriptora (na 80286 ta četvrta reč je obavezno bila 0) pa je dugačko ukupno 20 bita. 220 je tačno 1 megabajt što je, dakle, i maksimalna dužina segmenta!

Zvuči ubedljivo ali ipak nije tačno. U stvari, Intelovi inženjeri očito nisu imali prostora za čitavo polje limit pa su se snašli i uveli jedan fleg u poslednjoj reči deskriptora koji se zove G (granularity). Ako je taj fleg nula (a na 80286 je uvek nula, jer čitava poslednja reč deskriptora mora da ima vrednost nula), limit je izražen u bajtovima. Ako je G=1, limit je izražen u stranicama pri čemu je jedna stranica 4096 bajta. Kada pomnožite 220 sa 4096, dobijate tačno 232 odnosno 4 gigabajta što je maksimalna dužina segmenta na 80386/80486. Vešto smišljeno, zar ne?

12. Kada se stek prepuni, dešavaju se razne ružne stvari koje često rezultiraju potpunim padom sistema

Svakako istinito na starim Z80, 6502, 8086... srećom, tehnika napreduje. Pre svega, stek segment ima svoj deskriptor i svoj limit - čim se on pređe, proces se privremeno suspenduje i kontrola vraća operativnom sistemu koji može nešto da spasava ili da definitivno prekine program kome treba više steka nego što je predviđeno. Čitava provera obavlja se potpuno automatski i ne oduzima nikakvo vreme.

Prosta kontrola granica nije sve što 80486 nudi - uvedeni su i segmenti koji "rastu" zvani expand down segments. Za razliku od "normalnog" segmenta čije adrese počinju od 0 i rastu do polja limit pridruženog deskriptora, rastući segmenti počinju od limit+1 i rastu do FFFFFFFFh. Ukoliko je u takvom segmentu stek i ukoliko nastupi prekoračenje, operativni sistem može jednostavno da umanji vrednost limit-a (naravno, ako ispod ima prostora) i dopusti programu da se dalje izvršava. Sve ovo, jasno, važi samo ako se program sve vreme izvršavao u zaštićenom modu - u realnom naprosto sledi krah sistema!

13. U zaštićenom modu postoje dve vrste procesa - sistemski, kojima je "sve" dopušteno, i korisnički, koji podležu ograničenjima

80386 i 80486 omogućavaju i više od toga! Zapravo, postoje čak četiri nivoa - najprivilegovaniji nosi broj 0 i trebalo bi da se radi o samom jezgru operativnog sistema tj. o kodu kome je (da damo materijala za tekst "Zablude u tekstu o zabludama o 80486") sve dopušteno. Nivo 1 je "ostatak" operativnog sistema, nivo 2 su važni sistemski programi a nivo 3 korisnički programi sa najmanjim pravima.

To je teorija. U praksi uglavnom nema potrebe za ovolikim "detaljisanjem". Razne implementacije Unix-a, na primer, koriste samo dva nivoa privilegija - 0 je kernel a 3 korisnički programi. OS/2 (verzija 1.XX, još ne znamo kako stoje stvari na OS/2 2.0, ali najverovatnije nema izmena po tom pitanju) koristi tri nivoa - 0 je operativni sistem, 2 su programi koji smeju da pristupaju uređajima u sistemu (npr. serijskom portu itd) a 3 ostale aplikacije.

14. Programi koji se izvršavaju na višem prioritetnom nivou mogu da pristupaju podacima i pozivaju potprograme koji su na nižem prioritetnom nivou. I obrnuto - program na nižem prioritetu ne može da pristupa podacima niti da poziva potprograme višeg prioriteta

Nešto slično nigde nisam pročitao, ali nije da nema logike, bar na prvi pogled. Na drugi pogled, stvar je prilično sumnjiva - kako će, na primer, program nižeg prioriteta da zatraži neku uslugu operativnog sistema ako ne sme da pozove operativni sistem? A opet, ako mu dopustimo da poziva operativni sistem po volji, kako da budemo sigurni da ga neće "gađati" u neko nepredviđeno mesto (koje uopšte i nije ulazna tačka) i tako u boljem slučaju srušiti sistem, a u gorem obezbediti sebi neke privilegije koje ne bi smeo da ima?

Da vidimo kakva je stvarna situacija. Izrečena tvrdnja važi što se tiče podataka - program iz jezgra operativnog sistema (prioritet 0) može da pristupa svemu, program prioriteta 1 može da pristupa i podacima koji pripadaju programu prioriteta 2 i 3 i tako dalje. Nasuprot tome, program prioriteta 3 ne može da pristupa podacima koji pripadaju prioritetnim nivoima 0, 1 i 2.

Što se tiče potprograma, ograničenje je mnogo čvršće: program na bilo kom nivou ne može da poziva programe i potprograme na drugim nivoima. Na primer, program na nivou 1 ne može zvati potprograme na nivou 0 ali ni na nivoima 2 i 3. Čak ni jezgro operativnog sistema (nivo 0) ne može, verovali ili ne, da pozove najmanje privilegovan potprogram na nivou 3!

Na ovaj način je obezbeđena pristojna zaštita, ali se i dalje postavlja pitanje kako će aplikacija zatražiti uslugu operativnog sistema? Možda da mu pošalje pismo poput "moćni operativni sistemu, tvoja skromna i verna aplikacija želi da te zamoli da joj milostivo dodeliš još malo RAM-a za njen stek"? Ni takva poruka se, na žalost, ne bi mogla dostaviti jer između pojedinih nivoa postoje jaki "zidovi". Na svu sreću, na nekima od tih zidova otvorena su vrata...

Vrata (odnosno gate) su specijalni objekti (objekat je, kao i ranije, okarakterisan odgovarajućim deskriptorom, u zoni operativnog sistema) koji obezneđuju komunikaciju među procesima sa različitim privilegijama. Operativni sistem, na primer, može da odluči da u krugu 3 otvori jedna vrata koja pokazuju na neki potprogram nivoa 0. Obzirom da su vrata na nivou 3, aplikacija na tom nivou ih može "otvoriti" posle čega se kontrola predaje odgovarajućem potprogramu samog operativnog sistema; za vreme izvršavanja tog potprograma, proces privremeno dobija prioritet 0. Vredi primetiti da je na ovaj način omogućeno samo izvršavanje potprograma, a ne i njegovo čitanje, analiziranje, debagovanje... Takođe, ne može se pozvati "bilo šta" u operativnom sistemu, nego samo oni potprogrami na koje vrata pokazuju. Postoje četiri vrste vrata: call, interrupt, trap i task čije je značenje manje-više očigledno a opisi poprilično različiti. Uvedeni su i drugi bezbednosni mehanizmi, ali verujemo da ste već na osnovu ovog opisa zaključili na kom principu sve to funkcioniše i koliko je komplikovano.

15. 80486 je obezbeđen od svega, pa i od grešaka koje se jave tokom obrade drugih grešaka

Samo do neke granice. Naime, ukoliko se u toku obrade jednog interapta ili greške dogodi nešto jednako ozbiljno, sistem može da generiše takozvani double fault fault. Recimo, korisnik zahteva deljenje nulom, i čim krene obrada te greške pokaže se da na steku nema mesta za poziv odgovarajućeg programa. U tom slučaju automatski se poziva INT 08 koji treba da sredi stanje, uglavnom tako što će prekinuti izvršavanje programa. Jer, ako se tokom obrade double fault fault-a dogodi nova greška, mikroprocesor se blokira i čeka isključivo reset ili NMI - sve aplikacije koje su se prethodno izvršavale se nelegalno zaustavljaju. Mikroprocesor, doduše, tom prilikom generiše signal shutdown tako da se periferija može snalaziti... pa ipak, situacija je krajnje neprijatna.

Sve ovo ne bi nikako smelo da se desi na korektno napisanom operativnom sistemu, ali u fazi razvoja treba biti veoma pažljiv. Sam 80486 će vam maksimalno pomoći tako što "realne" double fault fault situacije tretira potpuno automatski pa ova greška ne nastupa - ukoliko, na primer, nastane greška, a program koji treba da je obradi trenutno nije u memoriji, najpre će biti izvšren page fault kako bi podaci stigli u memoriju, pa se tek onda program poziva, sve bez ikakve intervencije programera.

16. Nemaskirani interapt ima apsolutni prioritet i prekida sve što mikroprocesor radi. Nemaskirani interapt se ne može nikako onemogućiti

Nekad bilo. Kada govorimo o mikroprocesorima 80386 i 80486, NMI ima relativno nizak prioritet. Zapravo, "drugi od pozadi" - manje prioritetan od njega je jedino maskirani interapt INTR. Prva četiri prioriteta su redom (da se ne trudimo da ih prevedemo): nondebug fault, trap instruction, debug trap for the current instruction i debug fault for the pending instruction.

Ukoliko NMI i neko od ovih stanja nastupe istovremeno, prvo se obrađuje to stanje pa tek onda nemaskirani interapt. Dok se nemaskirani interapt sasvim ne obrati (IRET instrukcija), nastupanje novog nemaskiranog interapta je maskirano tj. onemogućeno. Kao što se vidi, konstruktori 80486 nisu imali mnogo poštovanja prema terminu nemaskirani interapt.

17. Intelovi mikroprocesori imaju jednobajtnu instrukciju INT 03 (kod 0CCh). Da bi neki debager napravio prekidnu tačku u programu, on jednostavno "zapamti" šta se tamo nalazilo a onda upiše 0CCh i tako obezbedi izlaz u trenutku nailaska na kritično mesto

Još jedna od "nekad bilo" stvari. Istini za volju, instrukcija 0CCh i dalje postoji i može se koristiti na opisani način. Ipak, 80486 to radi mnogo bolje i brže - u neki od registara DR0-DR3 upišete 32 bita neke adrese, podesite još neke flegove u DR6 i DR7 i naprosto očekujete da vam se kontrola vrati čim program koji "isleđujete" stigne dotle. Program čije izvršavanje pratite se ni u jednom trenutku ne menja, a na čitavu proveru se ne troši ni malo vremena - sve teče potpuno automatski.

Uopšte, konstruktori 80386 i 80486 su uložili ogroman napor da obezbede moćne i brze alatke za pronalaženje greški u programima.

18. Kada radite na nekom višekorisničkom sistemu (npr. VAX) sve vaše akcije se automatski "kontrolišu" i sistem dopušta samo ono na šta imate prava. Kada radite sa PC-jem, ceo računar je vaš!

Što se VAX-a tiče, istina. Što se PC-ja tiče... Ovo je jedna od onih čudnih tvrdnji koje su i u prvoj i u zadnjoj instanci tačne a u praksi su vrlo daleke od istine. Naime, ako upalite 80486 PC bez ikakvih rezidentnih programa (računajući tu i segmente DOS-a kao što je HIMEM.SYS, DOS=HIGH itd), zaista ste u realnom modu i možete dosta toga da radite. Ne baš sve, ali skoro sve. Takođe, u krajnjoj instanci, uz dosta znanja, truda i veštine možete da naterate računar da uradi ono što mu naredite. Ali u praksi, svaka vaša akcija je itekako kontrolisana.

Čim instalirate QEMM, 386MAX, EMM386, neki novi cache program ili nešto slično (a za šta bi služio 80386/80486 PC na kome nema nekog od ovih programa?), vaš mikroprocesor uopšte nije u realnom modu. Samo se uspešno pretvara - onih 640K memorije koje vidite možda uopšte nisu na početku adresne mape a možda čak i nisu fizički kontinualni, high memorija je skrpljena od parčića, svaki pristup RAM-u ide preko specijalnih drajvera da bi se simulirala LIM memorija... Pokušajte, na primer, da upišete nešto u "interesnu zonu QEMM-a" - poruka od nekoliko redova i vaš program je suspendovan! Ili još jednostavnije - setite se onoga što ste naučili na starim mikroprocesorima pa napunite status registar željenim sadržajem koristeći sekvencu MOV EAX, vrednost; PUSH EAX; POPFD i sve će lepo proći. Pa ipak, pogled u statusni registar će pokazati da je upisano samo ono što je operativni sistem smatrao da smete upisati - ako je neki fleg van vašeg domašaja, njegovo stanje se uopšte nije promenilo!

Ukratko, stavljen vam je na raspolaganje samo jedan virtualni PC koji je prividno pod vašom kontrolom a u stvari postoji nevidljiva "viša sila" koja upravlja stvarima. Pazite, vaš 80486 vas posmatra!