Remember, an int is not always 16 bits. I'm not sure, but if the 80386 is one step closer to Intel's slugfest with the CPU curve that is aymptotically approaching a real machine, perhaps an int has been implemented as 32 bits by some Unix vendors...?
-- Derek Terveer
![]() |
![]() |
![]() |
|
![]() |
Tento Φlßnek by m∞l poradit t∞m, kte°φ se rozhodli DJGPP otestovat, ale narazili na nejr∙zn∞j╣φ potφ╛e p°i pokusech zkompilovat svΘ star╣φ programy pomocφ GNU C. Popisuje v∞t╣inu problΘm∙, kterΘ nastanou p°i p°echodu z jin²ch p°ekladaΦ∙, zejmΘna Borland C. Je tedy zam∞°en na DOSovΘ u╛ivatele, ale snad pom∙╛e i ostatnφm. | ![]() |
|
![]() |
![]() |
![]() |
Toto je jedna z formulek, kterß je velmi Φasto omφlßna v r∙zn²ch Φasopisech. Dozvφme se, ╛e Windows 95 jsou 32 bitov² operaΦnφ systΘm, ╛e pou╛φvß 32 bitovou FAT a dokonce mß 32 bitov² p°φstup k disku. Kupodivu mßlokdo vφ, co to p°esn∞ znamenß a k Φemu to vlastn∞ je dobrΘ. Reklamy nßm pouze vsugerovaly, ╛e co je 32 bitovΘ, to je modernφ, rychlΘ, stabilnφ a dßvß pocit jistoty a bezpeΦφ. To je samoz°ejm∞ nesmysl.
Kolik bit∙ mß procesor se urΦuje v∞t╣inou podle ╣φ°ky datov²ch cest. Tedy kolik bit∙ se po takovΘ cest∞ v procesoru p°etßhne narßz. Z tohoto pohledu opravdu 32 bitovΘ procesory toho zvlßdnou vφce, ne╛ 16ti bitovΘ. Procesory Intelu jsou 32 bitovΘ od 386DX.
Situace ale nenφ tak jednoduchß. Pro 32 bitov² procesor je dobrΘ psßt k≤d, kter² t∞ch 32 bit∙ opravdu vyu╛ije. V╣echny procesory z °ady 80x86 star╣φ, ne╛ 386 byly 16ti bitovΘ (alespo≥ navenek) a proto i assemblerovΘ instrukce jsou takovΘ, ╛e 32 bit∙ nevyu╛ijou a poka╛dΘ zpracovßvajφ pouze 16ti bitovß data. Procesory 386 p°irozen∞ roz╣φ°ily tuto instrukΦnφ sadu a p°idaly 32 bitovΘ registry. Pokud pou╛ijete 32 bitov² registr na mφst∞ 16ti bytovΘho, poΦφtß se intern∞ v 32 bitech a teprve potom se vlastn∞ vyu╛ije cel² procesor. Je li n∞jak² k≤d 32 bitov², znamenß to vlastn∞ jenom to, ╛e namφsto 16ti bytov²ch registr∙ pou╛φvß i tyto 32 bitovΘ.
Takov² k≤d by m∞l b²t schopen vyu╛φt v╣echny schopnosti procesoru. Je ale otßzkou, jestli sΦφtßni 16 plus 16 provedenΘ 32 bitov∞ je rychlej╣φ, ne╛li stejnΘ sΦφtßnφ 16-ti bitov∞. Logickß odpov∞∩ je, ╛e nenφ. 32 bitovΘ sΦφtßnφ sice vyu╛ije cel² procesor, ale zase sΦφtß navφc jenom pßr zbyteΦnφch cifer, kterΘ jsou stejn∞ nulovΘ. Procesor toho musφ ud∞lat o n∞co vφc, a tak by to m∞lo b²t o n∞co pomalej╣φ. Dn∞╣nφ procesory ale provedou sΦφtßnφ jak 16-ti bitov∞, tak 32 bitov∞ v jednom taktu a tak by to m∞lo trvat stejn∞. Z tΘto ·vahy je vid∞t, ╛e zrychlenφ zp∙sobenΘ 32 bitov²m procesorem nep°φjde samo, ale je nutnΘ p°epsat programy tak, aby 32 bit∙ opravdu vyu╛ily.
Intel pro p°idßnφ 32 bitov²ch instrukcφ pou╛il takzvan² prefix. To je v∞c, kterß se napφ╣e p°ed instrukci a ta se rßzem zm∞nφ z 16-ti bitovΘ na 32 bitovou. Provedenφ tohoto prefixu ale trvß cel² takt. Tφm pßdem 32 bitovΘ sΦφtßnφ je dvakrßt pomalej╣φ. Na pentiu navφc tyto instrukce nemohou do druhΘ pipeliny a proto je provßd∞nφ k≤du s prefixy a╛ n∞kolikanßsobn∞ pomalej╣φ. Situace je je╣t∞ hor╣φ u nov∞j╣φch procesor∙.
Aby toho nebylo dost, vyvojß°i Intelu se rozhodli tento problΘm n∞jak obejφt. P°idali je╣t∞ mo╛nost nastavit Φßsti k≤du (segmentu) p°φznak, kter² urΦuje, jestli k≤d je 32 bitov². Pokud je tento p°φznak nastaven, instrukce jsou automaticky 32 bitovΘ (nepot°ebujφ tedy prefix). Naopak 16-ti bitovΘ instrukce ale prefix pot°ebujφ. Tφm pßdem naopak sΦφtßnφ v 16 ti bitech je zcela nelogicky dvakrßt pomalej╣φ, ne╛ 32 bitovΘ.
To mß prvnφ praktick² dopad pro programovßnφ. Narozdφl od
nap°φklad Borland C, GCC generuje 32 bitov² k≤d. Proto je i velikost typu
int
4 bajty. Provßd∞nφ k≤du obsahujφcφ int
by tedy melo b²t
p°ibli╛n∞ stejn∞ rychlΘ v Borland C i GCC pouze s tφm rozdφlem, ╛e v p°φpad∞
GCC budou v╣echny v²poΦty 32 bitovΘ a budou tedy brßt v∞t╣φ Φφsla.
Nenφ to ·pln∞ pravda. 32 bitovΘ programy v∞t╣inou dnes b∞╛φ o n∞co rychelji, proto╛e nov∞j╣φ procesory jsou v²razn∞ rychlej╣φ v p°φpad∞ 32 bitovΘho k≤du. GCC navφc lΘpe optimalizuje. Na druhou stranu ale 386 (zejmΘna SX) chroustß 32 bit∙ pomaleji.
Typy short
a long
ale z∙stßvajφ na obou p°ekladaΦφch
stejnΘ (16 a 32 bit∙) a proto k≤d pou╛φvajφcφ typ short
je na BC
ryhlej╣φ a naopak k≤d pou╛φvajφcφ long
je rychlej╣φ na GCC.
Z toho lze vyvodit, ╛e by se Φlov∞k m∞l sna╛it pou╛φvat v╣ude 32 bitovΘ
hodnoty, pokud mo╛no nepou╛φvat typ short
a nahradit je typem int
.
Dokonce i prom∞nΘ typu char
(8 bit∙) pot°ebujφ prefix.
Proto je Φasto lep╣φ poΦφtat s pou╛itφm typu int
.
Nenφ to ale ·pln∞ pravidlem, proto╛e 8-mi bitov²ch regitr∙ je vφce,
ne╛ 16-ti bitov²ch a 32 bitov²ch a proto se n∞kdy u╣et°φ prßce se zßsobnφkem.
Situace se je╣t∞ vφce komplikuje v p°φpad∞ pou╛itφ polφ.
N∞kdy se pole typu short
, Φi char
je╣t∞ vejde do cache
(kterß je na pentiu 4KB) zatφmco int
u╛ ne. P°φstup do takovΘ
pam∞ti je potom pomalej╣φ. Zde nezb²va nic jinΘho, ne╛ experimentovat.
Proto╛e jsou typy jinak dlouhΘ, mß to logicky dopad na velikost struktur, kterΘ se Φasto uklßdajφ na disk. Soubory ulo╛enΘ k≤dem z jednoho p°ekladaΦe pak nenaΦtete k≤dem z druhΘho. Navφc ale GCC struktury zarovnßvß. Od 486 se pro 32 bitovΘ hodnoty ulo╛enΘ na adresßch d∞liteln²ch Φty°mi pou╛ije 32 bitov² p°φstup. Pokud adresy d∞litelnΘ nejsou, pou╛ije se pomalej╣φ p°φstup. Proto se GCC sna╛φ v╣echny 32 bitovΘ hodnoty dr╛et na adresßch d∞liteln²ch Φφslem 4, a 16ti bitovΘ hodnoty na adresßch d∞liteln²ch Φφslem 2.
Proto╛e ale norma nedovoluje p°ehßzet polo╛ky ve strukturßch, GCC prost∞ p°ed ka╛dou polo╛ku p°idß volnΘ mφsto tak, aby adresa byla sprßvn∞ d∞litelnß v p°φpad∞, ╛e zaΦßtek struktury le╛φ na adrese d∞litelnΘ Φty°mi. Aby toto bylo zaruΦeno, zv∞t╣φ je╣t∞ strukturu tak, aby jejφ celkovß velikost byla nßsobek Φty°, aby i pole struktur bylo sprßvn∞ zarovnanΘ. Aby i zaΦßtky pole byly sprßvn∞ zarovnanΘ, i funkce pro alokaci pam∞ti (malloc atd.) vracφ adresy d∞litelnΘ Φφslem 4. Dφky tomu nap°φklad struktura:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; char b; int c; }; |
![]() |
|
![]() |
![]() |
![]() |
mß velikost 8 bajt∙. A
se ulo╛φ na zaΦßtek, b
hned za n∞j.
Potom se dva bajty vynechajφ, aby byla adresa c
d∞litelnß Φty°mi
a pak se ulo╛φ Φty° bajtovß hodnota C. Struktura je tedy stejnß, jako:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; char b; short unused; int c; }; |
![]() |
|
![]() |
![]() |
![]() |
Naopak ale struktura:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; int c; char b; }; |
![]() |
|
![]() |
![]() |
![]() |
zabφrß cel²ch 12 bajt∙, proto╛e za prom∞nou a
se vynechajφ
3 bajty pro zarovnßnφ a za poslednφm dal╣φ 3 bajty pro zarovnßnφ
velikosti struktury. Proto je ve strukturßch dobrΘ °adit polo╛ky
podle velikosti. U╣et°φ se tφm n∞jakΘ mφsto v pam∞ti.
Pokud tuto funkci pot°ebujete vypnout --- pot°ebujete strukturu
naΦφtat z disku tak, aby byla stejnß jako ta starß, nebo komunikovat s vnej╣φm
sv∞tem (vesa driver apod), je nutnΘ nastavit atribut packed
:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; int c; char b; } __attribute__ ((packed)); |
![]() |
|
![]() |
![]() |
![]() |
Tato struktura mß potom velikost 6 bajt∙. Navφc lze tento atribut pou╛φt uprost°ed struktury, pokud je t°eba pouze n∞jak² prvek p°ilepit hned za p°edchozφ:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; int c __attribute__ ((packed)); char b; } |
![]() |
|
![]() |
![]() |
![]() |
Toto zarovnßvßnφ lze takΘ urΦovat explicitn∞ pomocφ atributu
aligned
:
![]() |
![]() |
![]() |
|
![]() |
struct { char a; int c __attribute__ ((aligned (16))); char b; } |
![]() |
|
![]() |
![]() |
![]() |
Tady se int
zarovnß na 16 bajt∙ (stejn∞ jako velikost celΘ
struktury, aby to platilo i v poli). Velikost struktury tedy bude 32
bajt∙.
Tyto atributy lze pou╛φvat i na normßlnφ prom∞nΘ, pokud to je t°eba.
B∞╛n∞ se v programech Φφsla v∞t╣φ, ne╛ 65536 p°φli╣ nevyskytujφ a proto 32 bit∙ zas tak d∙le╛it²ch nenφ. Jedno z mφst, kde opravdu pom∙╛e je adresace. 64KB pam∞ti adresovateln²ch 16-ti bitov²m Φφslem je prost∞ mßlo. Procesory Intel v 16-ti bitovΘm m≤du toto obchßzejφ pomocφ segment∙:
Celß pam∞╗ je rozd∞lena na 64KB dlouhΘ bloky. Adresace potom probφhß tak, ╛e nap°ed zvolφte segment, kter² urΦuje blok a teprve potom offset, kter² urΦuje vlastnφ pozici v segmentu. Je tak tedy mo╛nΘ adresovat mnohem vφce pam∞ti. UrΦovßnφ v╣ech adres nadvakrßt by ale bylo pomalΘ a proto se pou╛φvajφ segmentovΘ registry. Ka╛d² program mß nastaven² nap°φklad registr CS (code segment). Pozice v programu se potom urΦuje pouze pomocφ offsetu a pou╛it²m segmentem je implicitn∞ CS. Podobn∞ funguje registr DS (data segment) pro data a dal╣φ registry. V╣echno funguje dob°e, pokud program nenφ del╣φ, ne╛ 64KB a nepou╛φvß vφce dat. V p°φpad∞, ╛e program je del╣φ, je nutnΘ pou╛φvat jinou instrukci pro skoky, volßnφ apod, kterΘ pou╛φvajφ plnΘ adresy (segment i offset) a podobn∞ u dat. Tyto instrukce jsou ale zase pomalej╣φ.
BC se s tφmto vypo°adßvß zavedenφm nekolika pam∞tov²ch model∙.
Jeden extrΘm je small, kde jsou v╣echny adresy krßtkΘ a nenφ
tedy mo╛nΘ mφt data Φi k≤d del╣φ, ne╛ 64Kb. V druhΘm extrΘmu
jsou zase v╣echny adresy dlouhΘ a k≤d je potom del╣φ a pomalej╣φ.
Mßte ale k dispozici celou pam∞╗. V∞t╣inou se pou╛φvajφ r∙znΘ
hybridnφ modely, kde n∞kterß data jsou blφzko a jinß daleko.
K tomu se pou╛φvajφ dva typy ukazatel∙. Do C je potom nutnΘ p°idat
slovφΦka far
a near
, pomocφ kter²ch se rozli╣uje mezi t∞mito typy.
Je takΘ t°eba vφcemΘn∞ zdvojit memorymanagement a p°idat farmalloc
pro alokaci vzdßlen²ch dat. I funkce p°ebφrajφcφ jako parametr
ukazatel by m∞ly b²t ve dvou verzφch --- pro far
i near
ukazatele.
TakΘ se tφm rozbije pointerovß aritmetika --- nenφ mo╛nΘ mφchat far
a
near
ukazatele. Prost∞ zaΦne tφm opravdovΘ peklo. Prßv∞ tato adresace
je d∙vodem, proΦ GCC 16-ti bitov² m≤d Intelovk²ch procesor∙ v∙bec nepodporuje.
Proradn², v²vojß°∙m firmy Intel ale ani to nestaΦilo a situaci je╣t∞ zdokonalili. U XT pou╛ili pouze pßr prvnφch bit∙ v segmentu jako adresu a proto XT umφ adresovat pouze 640KB pam∞ti. To ale brzo zaΦalo b²t mßlo a proto se do XT zaΦaly p°idßvat hardwarovΘ pam∞ti EMS, kterΘ ale nebyly b∞╛n∞ adresovatelnΘ. Bylo z nich vid∞t pouze okΘnko 64KB a pomocφ specißlnφho ovladaΦe se urΦovalo, kterou Φßst pam∞ti okΘnko zobrazuje. Pam∞╗ tak byla pomalej╣φ a prßce s nφ byla komplikovanß, ale alespo≥ n∞jakß byla.
V╣e je╣t∞ vφce zkomplikovaly procesory °ady 286. Zde je mo╛nΘ pou╛φt celΘ segmentovΘ adreasy a adresovat tak o n∞co vφce pam∞ti. Proto╛e ale XT vy╣╣φ bity adresy ignorovaly (tedy aresa 1 bylo to samΘ co adresa 640KB+1), p°estaly by fungovat n∞jakΘ hypotetickΘ obskurdnφ aplikace p°isupujφcφ na adresu 1 pomocφ tΘ del╣φ. Vznikla tedy ╣φlenost jmΘnem A20 gate (ten zmφn∞n² ignorovan² bit je prßv∞ 21.). Program pou╛φvajφcφ vφce, ne╛ 640KB RAM tedy nap°ed A20 gate zapne a potom m∙╛e adresovat dßl. B∞╛n∞ je ale A20 gate vypnutß a proto na╣e hypotetickß aplikace by mohla dßl fungovat myslφc si, ╛e je na XT.
To by ale nebylo tak stra╣nΘ jako to, ╛e existuje n∞kolik zp∙sob∙ p°epφnanφ tΘto brßny. Dobr² ovladaΦ je musφ v╣echny znßt. Be╛n∞ se o n∞ starß HIMEM.SYS. Proto po jeho zavedenφ mßte k dispozici Φßst pam∞ti jmΘnem HMA (high memory area). To je prßv∞ mφsto nad 640KB, kde lze normßln∞ provßd∞t k≤d, pokud je A20 zaplß.
286 ale u╛ umφ adresovat 16MB pam∞ti dokonce pomocφ n∞kolika berliΦek (himemu) ji i pou╛φvat. Pracuje se s nφ samoz°ejm∞ ·pln∞ jinak, ne╛ z p°φmo adresovatelnou dolnφ pam∞tφ, nebo EMS. Proto programy musφ znßt je╣t∞ dal╣φ zp∙sob jak p°istupovat do pam∞ti - XMS.
Snad Vßs pot∞╣φm, ╛e prßv∞ 32 bit∙ toto °e╣φ. 32 bitovß adresa
je dost dlouhß na to, aby adresovala celou pam∞╗. Proto nenφ t°eba
d∞lat ╛ßdnΘ segmenty, offsety, XMS, EMS, UMB, HMA, far
, near
,
pam∞╗ovΘ modely a dal╣φ stra╣ßky. Pokud je jednou pam∞╗
°ßdn∞ zinicalizovßna (to nenφ tak jednoduchΘ, u╛ t°eba dφky A20 gate),
je mo╛nΘ pomocφ jednoduchΘ 32bitovΘ adresy urΦit libovolnΘ
mφsto v pam∞ti stejn∞ rychle, jako v p°φpad∞ small modelu v reßlnΘm m≤du.
Dφky tomu m∙╛ete na v╣e co se t²kß t∞chto v∞cφ zapomenout.
Ve zdrojov²ch k≤dech smazat v╣echna slova far
a near
a radovat se z toho,
╛e mßte lineßrnφ pam∞╗. Navφc tφm padß omezenφ na velikost polφ,
proto╛e u╛ nenφ °ßdn² d∙vod, aby pole byla omezena na 64KB.
Dal╣φ v∞c, kterß odpadß jsou overlaye. Proto╛e velikost programu u╛ nenφ omezena volnou dolnφ pam∞tφ, m∙╛ete klidn∞ ud∞lat 2MB dlouh² program, ani╛ by bylo nutnΘ jej n∞jak porcovat.
Protected m≤d je to, co se v∞t╣inou skr²va pod reklamou na 32 bitov² operaΦnφ systΘm. SprßvnΘmu operaΦnφmu systΘmu by m∞lo b²t v principu jedno, na kolikabitovΘm procesoru b∞╛φ (vyjma limit∙ na n∞kterΘ v∞ci - jako soubory. Protected m≤d ale dßvß systΘmu novΘ a netu╣enΘ mo╛nosti.
Protected m≤d umo╛≥uje, aby jeden °φdφcφ programu zφskal kontrolu nad ostatnφmi. M∙╛e jim povolovat a zakazovat nejr∙zn∞j╣φ v∞ci a r∙zn∞ je tahat za nos. M∙╛e jim nap°φklad tvrdit, ╛e majφ sound blaster, v╣echny jejich p°φstupy na sound blaster chytit a provßd∞t na jinΘ zvukovΘ kart∞. TakΘ umo╛≥uje multitßsking - operaΦnφ systΘm m∙╛e procesu tvrdit, ╛e b∞╛φ na poΦφtaΦi sßm, ale p°esto jej kykoliv p°eru╣it a p°edat °φzenφ jinΘmu.
Jednou z nejkrßsn∞j╣φch v∞cφ protected m≤du je virtußlnφ pam∞╗. Pam∞╗, kterou program vidφ v∙bec nemusφ b²t pam∞tφ poΦφtaΦe. Pam∞╗ se rozd∞lφ na strßnky (4KB) a p°i p°φstupu k nφ, se nejprve zjistφ o jakou strßnku se jednß a potom se v tabulce vyhledß, kde danß strßnka ve fyzickΘ pam∞ti je.
Je tφm tedy mo╛nΘ vytvo°it specielnφ rozlo╛enφ pam∞ti pro ka╛d² proces. Od adresy 0 m∙╛e zaΦφnat jeho k≤d a od n∞jakΘ dal╣φ adresy ╛e zaΦφnß neomezenß volnß pam∞╗. Ostatnφ v∞ci v pam∞ti - systΘm, bios, dal╣φ procesy, videoram se p°ed programem schovß. Nem∙╛e tedy tyto v∞ci nijak ohrozit.
Navφc je mo╛nΘ strßnkßm p°i°adit r∙znß p°φstupovß prßva - nastavit n∞jakou Φßst pouze pro Φtenφ (nap°φklad k≤d sdφlenΘ knihovny), nebo pouze pro provßd∞nφ apod.
Proto╛e se operaΦnφ systΘm dozvφ v p°φpad∞, ╛e tyto p°φstupovß prßva byla poru╣ena, m∙╛e emulovat virtußlnφ pam∞╗. Pokud nenφ dost fyzickΘ pam∞ti, ulo╛φ se n∞jakΘ Φßsti programu na disk. Zablokuje potom p°φstup k tΘto Φßsti pam∞ti. V p°φpad∞, ╛e se program pokusφ tuto pam∞╗ pou╛φt, dojde k vyjφmce a operaΦnφ systΘm zase data naΦte z disku. Program tak nic nepoznß.
Volnß pam∞╗ potom m∙╛e b²t stejn∞ velkß, jako fyzickß plus volnΘ mφsto na disku dohromady.
Pokud proces chce p°istupovat nap°φklad k videoram, m∙╛e to sd∞lit operaΦnφmu systΘmu a ten mu potom vidoram m∙╛e p°idat do jeho pam∞ti p°idat tam, kam si proces °ekl.
Proto╛e DOS o protected m≤du nevφ, nejp°irozen∞j╣φ zp∙sob jak pod nφm provßd∞t protectedm≤dov² program je k n∞mu p°idat n∞jak² zavad∞Φ, kter² se nap°ed postarß o nastolenφ protected m≤du. To samo o sob∞ znamenß vypln∞nφ n∞kolika tabulek, vypnutφ A20 gate a p°epnutφ procesoru. Nenφ to nic stra╣nΘho.
ProblΘm ale je, ╛e DOS i BIOS je pouze 16-ti bitov² a v 32 bitovΘm
protected m≤dovΘm prost°edφ prost∞ nepob∞╛φ. Proto by takov² program
u╛ nemohl ani na disk. Proto vznikl re╛im jmΘnem V86. To je re╛im
procesoru, kdy se procesor tvß°φ, jako kdyby byl v normßlnφm (reßlnΘm)
re╛imu, ale p°esto je v protected m≤du. Tedy real modov² program (DOS a BIOS)
b∞╛φ, ale program zprost°edkujφcφ protected m≤d (v tomto p°φpad∞ to tedy
nenφ operaΦnφ systΘm) nad nimi mß stejnou kontrolu, jako nad normßlnφm
procesem v protected m≤du dokonce vΦetn∞ virtußlnφ pam∞ti. Tento re╛im
nap°φklad be╛n∞ pou╛φvajφ i programy jako EMM386, nebo QEMM pro emulaci
okΘnka do EMS. Nev²hodou tohoto re╛imu je, ╛e ne v╣echny instrukce procesor
zvlßdne sßm a n∞kterΘ se musφ emulovat (podobn∞ jako koprocesor
na 286). Tyto instrukce pak b∞╛φ samoz°ejm∞ pomalu. Mezi n∞ pat°φ
v∞t╣ina instrukcφ, kterΘ jsou normßln∞ privilegovanΘ a protected
m≤dovy program si je bez dovolenφ nem∙╛e dovolit - nap°φklad
cli
a sti
.
Proto 16ti bitovΘ programy pod V86 jsou pomalej╣φ a navφc v emulaci mohou
b²t chyby. Pro v∞t╣inu v∞cφ to ale staΦφ.
Proto╛e emulovat i interrupty a proto jsou systΘmovß volßnφ pracujφcφ hlavn∞ s interrupty docela pomalß. Je takΘ nutnΘ data pro n∞ p°ipravit do n∞jakΘho transfer bufferu, aby nßhodou nebyly mimo dolnφch 640 KB. Proto p°φstup k disku pod protected m≤dem hlavn∞ v p°φpad∞, ╛e p°istupujete po mal²ch kouscφch je v²razn∞ pomalej╣φ. 32 bitov² p°φstup k disku pod Windows d∞lß p°esn∞ to, ╛e nahradφ DOSovß volßnφ pro prßci s diskem 32 bitov²m k≤dem a tak se u╣et°φ n∞kolik p°echod∙ mezi protected a V86 m≤dem p°i p°φstupu k disku (volß se jenom BIOS). Nejednß se tedy o opravdov² 32 bitov² p°φstup (╛e by se IDE °adiΦ dostßval 32 bitovß Φφsla - co╛ je takΘ mo╛nΘ).
V p°φpad∞, ╛e toto v╣echno vy°e╣φte, po°ßd nenφ vyhrßno. Privilegovan² program °φdφcφ protected m≤d musφ b²t toti╛ pouze jeden. EMM386 a Qemm tento m≤d vyu╛φvajφ a proto Vß╣ program se pod nimi proste nespustφ. P°i pokusu o p°echod do protected modu zkolabuje. K tomu vzniklo roz╣φ°enφ VCPI. V p°φpad∞, ╛e je VCPI server v pam∞ti, nepou╛ijete standardnφ k≤d pro inicializaci protected m≤du, ale po╛ßdßte VCPI server, aby vßm p°edal °φzenφ. Ten to ud∞lß a vy tak zφskßte kontrolu. Nakonec ji zase p°edßte zpßtky.
Aby toho ale nebylo mßlo, nap°φklad Windows DOS prompt toto nepodporuje. Windows by potom ztratily by toti╛ mo╛nost provßd∞t multitßsking v p°φpad∞, ╛e p°edajφ °φzenφ. Proto byl vytvo°en nov² standard jmΘnem DPMI. DPMI znamenß DOS protected mode interface. DOS je v nßzvu je pon∞kud zavßd∞jφcφ, proto╛e DPMI s DOSem nemß nic spoleΦnΘho. Jinak je ale nßzev vpo°ßdku.
Jednß se o jak²si ovladaΦ, kter² na danΘm interruptu (podobn∞ jako t°eba ovladaΦ my╣i) sp°φstup≥uje nejb∞╛n∞j╣φ v∞ci, kterΘ protected m≤dov² program d∞lß (p°epφnanφ do proteted m≤du, zavolßnφ real modovΘho interruptu, prßce s virtußlnφ pam∞tφ atd...)
32 bitov² program tedy pak nepot°ebuje sv∙j vlastnφ k≤d pro inicializaci protected m≤du, V86 apod. Prost∞ a jednodu╣e volß DPMI.
Aby to ale nebylo tak snadnΘ, DPMI bylo navr╛eno pro 286, kterß u╛ m∞la jak²si protected m≤d, ale tak ne╣ikovny, ╛e ho stejn∞ nikdo nepou╛φval. Dφky tomu mß DPMI mnoho omezenφ. Navφc DPMI neb∞╛φ na v╣ech konfiguracφch a proto program stejn∞ musφ mφt i jinΘ cesty k inicializaci. Existuje n∞kolik verzφ DPMI a dφky velkΘ komplikovanosti standardu majφ skoro v╣echny implementace n∞jakΘ nedostatky.
Pokud ale mßte ╣t∞stφ, DPMI je dostupnΘ a funguje, mßte lineßrnφ pam∞╗, mo╛nost, volat realmodovΘ interrupty a dal╣φ nezbytnΘ v∞ci. Jedna z nejv∞t╣φch komplikacφ je pouze naprosto zbyteΦnΘ omezenφ na velikost zßsobnφku (zßsobnφk se musφ urΦit na zaΦßtku).
Abych shrnul poznatky z p°edchozφch odtavc∙. Pokud chcete mφt program v protected m≤du, musφte zvlßdnout jeho inicializaci, V86 re╛im, VCPI a DPMI.
To je d∙vod, proΦ be╛n∞ vznikajφ extendery, co╛ jsou externφ programy, kterΘ se postarajφ o nastartovßnφ protected m≤du a spu╣t∞nφ 32 bitovΘ aplikace. Ve Watcom c to je znßm² Dos4GW, v EMX/GCC to je EMX a RSX, ve starΘm DJGPP to bylo go32 apod.
V DJGPP v2 se rozhodli to ud∞lat jinak. Programy kompilovanΘ pod DJGPP jsou p°φmo DPMI clienty a pou╛φvajφ DPMI jako ,,extender'' . V p°φpad∞, ╛e pou╛φvßte Windows, nebo Qemm s DPMI, nenφ tedy t°eba ╛ßdn² externφ program. V p°φpad∞, ╛e DPMI server neb∞╛φ, program se pokusφ pou╛φt externφ program (CWSDPMI). Teprve v p°φpad∞, ╛e ani to nejde, napφ╣e hlß╣ku o tom, ╛e pot°ebuje DPMI a skonΦφ. Proto╛e b∞╛n² extender musφ b∞╛et pod DPMI a m∙╛e tedy obsahovat pouze podmno╛inu jeho slu╛eb, zdß se mi to jako dobrΘ °e╣enφ.
CWSDPMI je velmi kvalitnφ implementace DPMI - b∞╛φ tΘm∞° v╣ude, je relativn∞ krßtk² (20KB) a umφ velkΘ mno╛stvφ DPMI volßnφ. Neemuluje v∙bec 16-ti bitovΘ DPMI, to ale v p°φpad∞ DJGPP nenφ t°eba.
D∙le╛itß v∞c je, ╛e nesmφte zapomenout p°ibalit CWSDPMI ke v╣em program∙m.
Je mo╛nΘ je╣t∞ pou╛φt mezi u╛ivateli Watcom C velmi roz╣φ°en² DPMI server
pmode
. Ten je je╣t∞ krat╣φ a o n∞co mßlo rychlej╣φ. Existuje takΘ
utilita, co jej p°idß p°φmo do EXE souboru, co╛ n∞kte°φ pova╛ujφ za
profesionßln∞j╣φ. Ja osobn∞ to pova╛uju za zbyteΦnΘ pl²tvßnφ mφstem na disku.
Pmode ale neemuluje celΘ DPMI, je pom∞rn∞ nesnß╣enliv² a neumφ
swapovat na disk. Proto jej nedoporuΦuju.
Vlastnφ distribuce CWSDPMI obsahuje je╣t∞ dva programy. Prvnφm je CWSDPR0. Pokud pou╛ijete tento DPMI server, program b∞╛φ v privilegovanΘm m≤du, proto je mo╛nΘ nap°φklad pou╛φt Φasovacφ instrukce na pentiu. Ale pro b∞╛nΘ pou╛itφ to je kniΦemu. Nem∙╛e swapovat na disk, neodpovφdß p°esn∞ DPMI specifikaci apod.
Druh²m programem je CWSPARAM. Ten umo╛≥uje nastavit mnoho
u╛iteΦn²ch v∞cφ. Jako prvnφ je cesta k swapovacφmu souboru. B∞╛n∞
se swapuje na c:
, ale nap°φklad u diskless stanic je cestu t°eba p°enastavit.
Dal╣φ d∙le╛φtß v∞c je polo╛ka Minimum application memory desired before
640K paging
. B∞╛n∞ toti╛ DPMI programy v∙bec nepou╛φvajφ pam∞╗ pod
640KB. N∞kdy se stßvß, ╛e tΘm∞° cela XMS je pou╛ita pro cache apod.
Tato polo╛ka urΦuje minimßlnφ velikost XMS, kdy se je╣t∞ dolnφ pam∞╗
nepou╛ije. Pokud Vß╣ program nepou╛φvß ╛ßdnΘ specißlnφ DOSovΘ tryky, kterΘ
pot°ebujφ pam∞╗ (jako t°eba zavßd∞nφ resident∙ Φi DOS prompt),
je dobrΘ tuto hodnotu nastavit na maximßlnφ velikost pam∞ti, kterou Vß╣ programpot°ebuje.
Dal╣φ polo╛ka urΦuje kolik pam∞ti se mß ponechat pro DOS v p°φpad∞,
╛e se zaΦne dolnφ pam∞╗ pou╛φvat. Pokud nepou╛φvßte DMA, soubory atd.,
je mo╛nΘ tuto hodnotu nastavit dokonce na 0. Jinak je standardnφ nastavenφ
p°im∞°enΘ.
Tento program spolu s dokumentacφ k CWSDPMI je take dobrΘ p°ibalit k Va╣emu programu. U╛ivatelΘ pak majφ mo╛nost nastavit si parametry dle libosti.
V²sledkem linkovßnφ nenφ DOSov² EXE sobor, jako v Borland C,
ale COFF objekt. K jeho su╣t∞nφ jde pou╛φt zavad∞Φ GO32V2, nebo
k n∞mu p°idat tzv. stub. Stub je krßtk² 16ti bitov² k≤d, kter² se postarß
o sprßvnΘ zavedenφ 32 bitovΘho objektu, naΦtenφ CWSPMI (pokud
to je t°eba), p°epnutφ do protected modu a odstartovßnφ vlastnφho
programu. Tento stub se k programu p°idßvß programem stubify
. Navφc
mß ale n∞kolik standardnφch voleb, kterΘ lze p°enastavit programem
stubedit
. Z Makefile to lze d∞lat pomocφ parametr∙ z p°φkazovΘ
°ßdky. Pokud ╛ßdnΘ parametry nezadßte, program o n∞ po╛ßdß interaktivn∞.
Lze nastavit velikost zßsobnφku (mimochodem u p°ekladaΦ∙ C a C++
Md cc1
je n∞kdy nutnΘ tuto velikost zv∞t╣it, pokud odmφta p°elo╛it
n∞jak² slo╛it∞j╣φ program).
Dal╣φ parametr je velikost transfer bufferu. To je buffer pou╛φvan² pro volßnφ DOSov²ch slu╛eb (t°eba kdy╛ pot°ebujete ulo╛it n∞co do souboru, data se nap°ed zkopφrujφ do tohoto bufferu a teprve potom se volß DOS). U program∙, kterΘ Φasto vym∞≥ujφ data s DOSem je dobrΘ tuto velikost zv∞t╣it (pom∙╛e to t°eba u preprocesoru a linkeru na sφti)
Pomocφ parametru Base name of file to actualy run
lze
vytvß°et napodobeninu symbolick²ch link∙. Pokud nenφ nastaven na prßzdn²
°et∞zec, nezavede se program z toho samΘho souboru, ale z jinΘho. Pod UNIXem
je b∞╛nΘ, aby jeden program m∞l dv∞ jmΘna. Pokud to pot°ebujete pod
DJGPP, jde program ulo╛it do jednoho souboru a potom vytvo°it druh²
soubor, kter² obsahuje pouze stub s nastaven²m tφmto parametrym.
Value to pass as argv[0]
- co se programu mß p°edat jako jmΘno.
Pokud je prßzdn² °et∞zec, pou╛ije se jmΘno souboru.
Program to provide DPMI server
- jak² program se mß volat,
v p°φpad∞, ╛e DPMI server neni k dispozici. Standardn∞ to je
CWSDPMI.
Programy pod GCC jsou 32 bitovΘ, proto nelze pou╛φt b∞╛nΘ
emulßtory koprocesoru. Pokud pou╛φvßte n∞jak² floating point k≤d
a chcete, aby program fungoval i na 386, je nutnΘ p°idat emulßtor.
Ten lze zalinkovat p°φmo do k≤du pomocφ knihovny emu (p°epφnaΦ
-lemu
) pro GCC, nebo zavßd∞t z externφho modulu emu387.dxe
.
Pokud p°idßte emu387.dxe
do stejnΘho adresß°e, jako je EXE
soubor, m∞lo by v╣echno chodit. Pokud je jinde, je nutnΘ nastavit
prom∞nnou:
![]() |
![]() |
![]() |
|
![]() |
set emu387=c:/djgpp/bin/emu387.dxe |
![]() |
|
![]() |
![]() |
![]() |
N∞kdy stßvß (alespo≥ si na to na mailing listu kdysi dßvno n∞kdo st∞╛oval), ╛e program ╣patn∞ zdetekuje, ╛e chybφ koprocesor. Potom pom∙╛e nastavenφ:
![]() |
![]() |
![]() |
|
![]() |
set emu387=c:/djgpp/bin/emu387.dxe |
![]() |
|
![]() |
![]() |
![]() |
Po provedenφ stubu se dostane ke slovu zavßd∞ci k≤d. Ten mß
za ·kol v╣echno p°ipravit a zavolat vlastnφ main
. D∞lß doho o n∞co
vφc, ne╛ je zvykem. DJGPP se tφm sna╛φ docφlit v∞t╣φ kompatibility s
UNIXem. (V UNIXu nap°φklad expanze p°φkazovΘ °ßdky (*
a dal╣φ znaky)
nejsou v∞cφ programu, ale v∞cφ shellu, kter² program volß.) DJGPP
tedy expanduje tyto znaky automaticky p°i startu, aby programy u╛
dostaly parametry stejn∞ jako v UNIXu. Tato automatickß
konverze je obΦas ne╛ßdoucφ, proto╛e pak nem∙╛ete p°edat programu
parametry obsahujφcφ znaky jako *
.
Dφky tomu jsou takΘ v²slednΘ EXE soubory o n∞co v∞t╣φ. V∞t╣ina funkcφ jde ale vypnout v p°φpad∞, ╛e nejsou t°eba. Proto lze velikost v²slednΘho EXE souboru zm∞n╣it na 6KB, co╛ nenφ tak stra╣nΘ vzhledem k tomu, co v╣e se p°i inicializaci d∞je. DJGPP je tedy i docela vhodnΘ pro psanφ krßtk²ch utilit.
V╣echny prototoypy pro tento k≤d sφdlφ v headeru crt0.h
.
R·znΘ parametry pro start programu lze nastavit pomocφ prom∞nΘ
_crt0_startup_flags
. Nastavφte jφ tak, ╛e do svΘho programu napφ╣ete
int _crt0_startup_flags=n∞co
. Funkce musφ b²t globßlnφ (tedy mimo
funkci a bez static
). Jde nastavit nßsledujφcφ flagy:
_CRT0_FLAG_PRESERVE_UPPER_CASE
Pokud tento flag je nastaven,
nep°evede se argv[0]
(jmΘno programu) na malß pφsmena.
_CRT0_FLAG_USE_DOS_SLASHES
Nep°evßdφ se v argv[0]
opaΦnß
lomφtka na normßlnφ
_CRT0_FLAG_DROP_EXE_SUFFIX
Pokud je nastaven, vynechß se p°φpona .exe
v argv[0]
_CRT0_FLAG_DROP_DRIVE_SPECIFIER
pokud je nastaven, vynechß se jmΘno disku v argv[0]
(pokud je uvedeno)
_CRT0_FLAG_DISALLOW_RESPONSE_FILES
Standardn∞ zavad∞cφ k≤d projde
parametry a pokud najde parametr ve formßtu @jmeno
, p°eΦte si
parametry se souboru jmeno
a dosadφ je. Pokud je tento flag
nastaven, nic takovΘho se nestane.
_CRT0_FLAG_FILL_SBRK_MEMORY
Pokud je nastaven, ka╛dß pam∞t p°idßvanß do programu se nap°ed snuluje. To pot°ebujφ n∞kterΘ chybnΘ programy z UNIXU, proto╛e tam se to d∞je automaticky (kv∙li bezpeΦnosti)
_CRT0_FLAG_FILL_DEADBEEF
Pam∞╗ nenuluje, ale nastavuje na hodnotu
0xdeadbeef
, co╛ umo╛≥uje vychytat chyby.
_CRT0_FLAG_NEARPTR
Pokud je nastaven, od adresy __djgpp_nearptr_base
se sp°φstupnφ fyzickß pam∞╗. Vypne to ochranu pam∞ti a proto se
to nedoporuΦuje.
_CRT0_FLAG_NULLOK
Vypne odchytßvßnφ p°φstupu na ukazatel NULL
.
_CRT0_FLAG_NMI_SIGNAL
Pokud se nastavφ, nejsou MNI signßly propou╣t∞ny do 16ti bitovΘho k≤du. To obΦas zp∙sobuje potφ╛e s green BIOSy
_CRT0_FLAG_NO_LFN
Vypne podporu pro dlouhΘ nßzvy pod W95
_CRT0_FLAG_UNIX_SBRK
Za°φdφ, aby se sbrk chovalo jako v UNIXu. Tedy aby v╣echna pam∞╗ byla zasebou. To ale obΦas vy╛aduje p°esouvßnφ blok∙ pam∞ti a takΘ n∞kterΘ DPMI servery neumo╛≥ujφ tak naalokovat celou pam∞╗.
_CRT0_FLAG_LOCK_MEMORY
Uzamkne cel² program v pam∞ti a zakß╛e swapovßnφ. To je obΦas t°eba, kdy╛ chcete psßt ovladaΦe interrupt∙ a nechcete je zamykat ruΦn∞. Vypne to ale virtußlnφ pam∞╗ a tak to nenφ dobr² nßpad. Podle m²ch zku╣enostφ tento flag n∞jak nefunguje.
_CRT0_FLAG_PRESERVE_FILENAME_CASE
Vypne p°evod nßzv∙ z velk²ch pφsmen na malß.
Nastavenφm tΘto prom∞nΘ sice m∙╛ete vypnout r∙znΘ funkce, ale po°ßd se zalinkujφ do programu. Pokud chcete urΦitΘ kusy k≤du vynechat, je nutnΘ mφsto nich napsat prßzdnΘ funkce, kterΘ potom linker pou╛ije mφsto t∞ch originßlnφch.
Startovacφ k≤d mimo zavßd∞nφ programu je╣t∞ d∞lß:
*
, @
apod)Vypnutφ zavedenφ souboru s environmentem (to jde ud∞lat skoro ve v╣ech programech, kterΘ nemajφ zapadnout do originßlnφho v²vojovΘho prost°edφ DJGPP) se provede pomocφ:
![]() |
![]() |
![]() |
|
![]() |
void __crt0_load_environment_file(char *_app_name) { return; } |
![]() |
|
![]() |
![]() |
![]() |
Expanze specißlnφch znak∙ se provßdφ pomocφ:
![]() |
![]() |
![]() |
|
![]() |
char **__crt0_glob_function(char *_arg) { return 0; } |
![]() |
|
![]() |
![]() |
![]() |
Pomocφ tΘto funkce m∙╛ete nastavit i sv∙j vlastnφ expander.
P°φpravu argv
a argc
provßdφ funkce:
![]() |
![]() |
![]() |
|
![]() |
void __crt0_setup_arguments(void); |
![]() |
|
![]() |
![]() |
![]() |
Pokud vß╣ program nepou╛φvß parametry, je mo╛nΘ ji nahradit za prßzdnou funkci.
Dßle lze z programu vylouΦit exception handling (program potom v p°φpad∞ ╛e nap°φklad vyd∞lφ nulou nevypφ╣e registry, ale zatuhne)
![]() |
![]() |
![]() |
|
![]() |
void _npxsetup(void) { return; } int __emu387_load_hook; short __djgpp_ds_alias; void __djgpp_exception_setup(void) { return; } int __djgpp_set_ctrl_c(int enable) { return 0; } |
![]() |
|
![]() |
![]() |
![]() |
Poslednφ ╣ikovnß prom∞nß je _stklen
, potomcφ kterΘ m∙╛ete
nastavit velikost zßsobnφku bez pou╛φtφ programu stubedit
.
Mimochodem jedna z metod jak zmen╣it velikost v²slednΘho souboru
je nezalinkovat debugovacφ informace pomocφ p°epφnaΦe -s
, nebo je
zru╣it pomocφ programu strip
.
Pokud pot°ebujete p°φstup k n∞jakΘ fyzickΘ pam∞ti (t°eba videoram), nenφ nic jednodu╣╣φho, ne╛ po╛ßdat DPMI, aby ji dalo do adresovatelnΘho prostoru Va╣eho programu. Nane╣t∞stφ toto je funkce DPMI 1.0 a vy╣╣φ a tak ji v∞t╣ina DPMI server∙ nepodporuje. Proto musφ zaΦφt magie.
I v protected m≤du existujφ segmenty a offsety. Segmenty jsou tak velkΘ (celß adresovatelnß pam∞╗), ╛e se pou╛φvß jenom jeden segment. V DPMI ale program b∞╛φ uvnit° jednoho segmentu, zatφmco fyzickß pam∞╗ je v jinΘm segmentu. Proto lze pou╛φt segmentace pro p°φstup do vn∞j╣φ pam∞ti.
Segment dosu je ulo╛en v prom∞nnΘ _dos_ds
a existujφ funkce
_farpoke*
a _farpeek*
kterΘ umo╛≥ujφ p°istupovat na tyto vzdßlenΘ
ukazatele (_farpokeb
p°istupuje k bajtu, _farpokew
k wordu atd.) Funkce se p°i zapnutΘ optimalizaci inlinujφ a jsou implementovßny takto:
![]() |
![]() |
![]() |
|
![]() |
extern __inline__ void _farpokeb(unsigned short selector, unsigned long offset, unsigned char value) { __asm__ __volatile__ ("movw %w0,%%fs\n" " .byte 0x64 \n" " movb %b1,(%k2)" : : "rm" (selector), "qi" (value), "r" (offset)); } extern __inline__ void _farpokew(unsigned short selector, unsigned long offset, unsigned short value) { __asm__ __volatile__ ("movw %w0,%%fs \n" " .byte 0x64 \n" " movw %w1,(%k2)" : : "rm" (selector), "ri" (value), "r" (offset)); } |
![]() |
|
![]() |
![]() |
![]() |
T∞ch lze pou╛φt pro adresaci videoram asi takto:
![]() |
![]() |
![]() |
|
![]() |
#define putpixel(x,y,c) _farpokeb(_dos_ds, 0xA0000 + (y)*320 + (x), (c)) |
![]() |
|
![]() |
![]() |
![]() |
To sice funguje, ale zbyteΦn∞ se poka╛dΘ nastavuje fs
.
Jednodu╣╣φ je ho nastavit jednou na zaΦßtku. K tomu slou╛φ funkce
_farsetsel
a _farnspoke*
.
Na zaΦßtek vykresolvacφ smyΦky p°idßte: _farsetsel(_dos_ds)
a potom
kreslφte pomocφ _farnspokeb(0xA0000 + y*320 + x, color);
Toto je asi nejΦast∞ji pou╛φvanß metoda pro p°φstup k pam∞ti. Funguje pod ka╛d²m DPMI a je docela rychlß. Existujφ takΘ far verze pro kopφrovßnφ pam∞ti, tak╛e kdy╛ n∞co nakreslφte do framebufferu, nenφ probΘm to potom zkopφrovat do VRAM. To lze takΘ d∞lat hned dv∞ma zp∙soby:
![]() |
![]() |
![]() |
|
![]() |
dosmemput(doublebuffer, 320*200, videoptr); movedata(_my_ds(), doublebuffer, _dos_ds, videoptr, sizeof(*doublebuffer)); |
![]() |
|
![]() |
![]() |
![]() |
Dal╣φ metoda je zp°φstupn∞nφ celΘ dosovΘ pam∞ti. To naprosto vypne ve╣kerou ochranu pam∞ti a proto se to nedoporuΦuje. Pokud ale pot°ebujete opravdu rychle no RAM, je to jedna z mo╛nostφ. Vypadß to asi takto:
![]() |
![]() |
![]() |
|
![]() |
#include <sys/nearptr.h> unsigned char *videoptr = (unsigned char *)0xA0000; __djgpp_nearptr_enable(); videoptr[y*320 + x + __djgpp_conventional_base] = color; __djgpp_nearptr_disable(); |
![]() |
|
![]() |
![]() |
![]() |
Je je╣t∞ nutnΘ si pamatovat, ╛e __djgpp_conventional_base
je
prom∞nnß a m∙╛e se m∞nit po ka╛dΘm zavolßnφ alokace pam∞ti.
Obojφ je velmi snadnΘ. O p°φstup na porty se starajφ inlinovanΘ
funkce outport*(port,hodnota)
a inport*(port)
(*
je b
pro byte,
w
pro word a l
pro long). Funkce jsou definovanΘ v pc.h
.
![]() |
![]() |
![]() |
|
![]() |
void setpalette(char color, struct rgbstruct rgb) { outportb(0x3c8, color); outportb(0x3c9, rgb.red); outportb(0x3c9, rgb.green); outportb(0x3c9, rgb.blue); } |
![]() |
|
![]() |
![]() |
![]() |
Proto╛e se funkce inlinujφ, nenφ nutnΘ kv∙li tomu psßt assembler - v²sledek je ·pln∞ stejn².
Pro volßnφ realmode interrupt∙ existuje DPMI slu╛ba. Funkce pro
nφ je v dpmi.h
a jmenuje se __dpmi_int
. Jejφ pou╛itφ je nßsledujφcφ:
![]() |
![]() |
![]() |
|
![]() |
void setmode(short mode) { __dpmi_regs r; r.x.ax = mode; __dpmi_int(0x10,&r); } |
![]() |
|
![]() |
![]() |
![]() |
O nastavovßnφ interruptu se starß DPMI server. Pokud chcete pov∞sit n∞jakou funkci na interrupt, je nutnΘ ud∞lat n∞kolik v∞cφ. Nejd∙le╛it∞j╣φ je zabrßnit swapovßnφ. IntelovΘ toti╛ virtualnφ pam∞╗ v tomto p°φpad∞ dost zkomplikovali. Pokud je vyvolßn hardwarov² interrupt (ten co odchytßvßte) a z n∞ho se vyvolß dal╣φ (v²padek strßnkovßnφ), procesor spanika°φ a vyvolß se automaticky interrupt 8 - dvojitß chyba. Z tohoto interruptu u╛ nenφ nßvratu a v∞t╣inou zp∙sobφ, ╛e se poΦφtaΦ zniΦeho nic vyrezetuje.
Nevidφm ╛ßdn² logick² d∙vod pro toto chovßnφ. Nßsledek ale je,
╛e nenφ mo╛nΘ, aby se ovladaΦ pre°u╣enφ odswapoval na disk. Proto
je nutnΘ DPMI serveru °φct, ╛e tento kus pam∞ti se nesmφ swapovat
(uzamknout). Nejjdedu╣╣φ °e╣enφ je zablokovat celΘ swapovßnφ,
pomocφ flagu _CRT0_FLAG_LOCK_MEMORY
. TakovΘ zamykßnφ naveliko ale
rozhodn∞ nenφ dobr² nßpad a hlavn∞ multitßskovΘmu OS m∙╛e po°ßdn∞
zkomplikovat ╛ivot.
Je mo╛nΘ takΘ zamykat jednodlivΘ strßnky pam∞ti pomocφ funkcφ
_go32_dpmi_lock_data
a _go32_dpmi_lock_code
. Allegro
definuje nßsledujφcφ makro pro zamykßnφ prom∞n²ch:
![]() |
![]() |
![]() |
|
![]() |
#define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void *)&x, sizeof(x)) |
![]() |
|
![]() |
![]() |
![]() |
Toto makro je nutnΘ zavolat na v╣echny prom∞nΘ, kterΘ handler
pou╛φvß. Navφc je nutnΘ zamknout vlastnφ funkci handleru (a v╣echny
funkce, co volß). Nejde ale zjistit velikost funkce pomocφ sizeof
.
Allegro to °e╣φ tak, ╛e za koncem funkce se napφ╣e dal╣φ prßzdnß
funkce pomocφ makra:
![]() |
![]() |
![]() |
|
![]() |
#define END_OF_FUNCTION(x) void x##_end() { } |
![]() |
|
![]() |
![]() |
![]() |
A makro pro zamykßnφ funkcφ potom uzamkne oblast mezi funkcφ
jmeno
a funkcφ jmeno_end
:
![]() |
![]() |
![]() |
|
![]() |
#define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x, (long)x##_end - (long)x) |
![]() |
|
![]() |
![]() |
![]() |
Vlastnφ handler p°eru╣enφ vypadß naprosto stejn∞ jako normßlnφ funkce:
![]() |
![]() |
![]() |
|
![]() |
void int8(void) { unsigned offset = ScreenPrimary+(2*79)+1; _farsetsel(_dos_ds); _farnspokeb(offset,1+_farnspeekb(offset)); } END_OF_FUNCTION(int8) |
![]() |
|
![]() |
![]() |
![]() |
Metod, jak nainstalovat handler na preru╣enφ je n∞kolik.
Normßln∞ by se Φlov∞k je╣t∞ musel postarat o uklßdßnφ registr∙ atd. To
jde obejφt pomocφ funkce _go32_dpmi_allocate_iret_wrapper
. Ta vyrobφ
krßtkou assemblerovou funkci, kterß se o to postarß. Pou╛itφ vypadß
asi takto:
![]() |
![]() |
![]() |
|
![]() |
_go32_dpmi_seginfo info; info.pm_offset = int8; _go32_dpmi_allocate_iret_wrapper(&info); _go32_dpmi_set_protected_mode_interrupt_handler(8, &info); ... _go32_dpmi_free_iret_wrapper(&info); /*uvolnφ pam∞╗ pou╛φtou wrapperem*/ |
![]() |
|
![]() |
![]() |
![]() |
Je╣t∞ by ale bylo nutnΘ se starat o volßnφ p∙vodnφho handleru.
Pokud chcete aby i o to se postarala knihovna, staΦφ pou╛φt
funkci _go32_dpmi_chain_protected_mode_interrupt_vector
ta se sama
postarß o wrapery a volßnφ:
![]() |
![]() |
![]() |
|
![]() |
_go32_dpmi_seginfo pmint; pmint.pm_selector = _my_cs(); pmint.pm_offset = (unsigned)&int8; _go32_dpmi_chain_protected_mode_interrupt_vector(8, &pmint); |
![]() |
|
![]() |
![]() |
![]() |
u╛ v╣echno za°φdφ a obrazovka zaΦne p∞kn∞ blikat. Ale jenom a╛ do chvφle, dokud se DPMI nepokusφ o swapovßnφ. Nenφ toti╛ pam∞t zamknutß. To se ud∞lß takto:
![]() |
![]() |
![]() |
|
![]() |
LOCK_FUNCTION(int8); LOCK_VARIABLE(ScreenPrimary); |
![]() |
|
![]() |
![]() |
![]() |
TakΘ je nutnΘ se ujistit, ╛e optimalizace jsou zapnutΘ a ╛e se funkce pro p°φstup do VRAM inlinujφ.
Z handelr∙ nelze skßkat pomocφ longjmp
a nenφ dobr² nßpad volat
knihovnφ funkce jako printf
.
Samoz°ejm∞, ╛e assemlerovΘ wrappery tro╣ku zdr╛ujφ. Pokud vßm jde o Φas, je mo╛nΘ si v╣echno napsat ruΦn∞. Tak to d∞lß nap°φklad allegro a tak nenφ nic jednodu╣╣φho, ne╛ se podφvat do zdrojov²ch k≤d∙. Proto╛e se o interrupty ale starß DPMI, nelze se spolehnout na to, ╛e budou rychlΘ. Pokud pou╛ijete CSDPMI, je mo╛nΘ takto napsat i ovladaΦ od pc-speakeru (40000 interrupt∙ do sekundy). Nap°φklad ale pod Qemm DPMI to u╛ nestihne.
Pou╛itφ inline assembleru Je popsßno v Φlßnku o roz╣φrenφch GCC. Proto
se nynφ budu zab²vat linkovßnφm assemblerov²ch objet∙. Pro psanφ
assembleru jde pou╛φt bu∩ standardnφ program gas
(GNU assembler).
Ten pou╛φvß AT&T syntax, kterou jsem takΘ popsal ve zvlß╣tnφm Φlßnku,
nebo program NASM
, kter² mß syntax (a╛ na drobnΘ vyjimky)
shodnou s programem TASM a proto asi bude lep╣φ v p°φpad∞, ╛e mßte n∞jak²
assemblerov² k≤d a chcete jej pou╛φt. Dφky rozdφl∙m mezi 16 a 32
bitov²m k≤dem stejn∞ ale bude nutnΘ ud∞lat n∞jakΘ zm∞ny.
Existuje takΘ p°evad∞Φ ta2as
, kter² se starß o p°eklad z Intelφ
syntaxe do AT&T. Nenφ ale p°φli╣ spolehliv².
Dßle budu psßt hlavn∞ o programu gas
, proto╛e NASM nepou╛φvßm
Asi jeden z nejjedodu╣╣φch zp∙sob∙, jak se nauΦit gas
je
pou╛φt jednoduch² program v C jej do assembleru (pomocφ -S
).
Zßkladnφ struktura souboru je asi nßsledujφcφ:
![]() |
![]() |
![]() |
|
![]() |
.file "jmΘno" .data mojedato: .word 0 .text .globl __myasmfunc __myasmfunc: ... ret |
![]() |
|
![]() |
![]() |
![]() |
Vlastnφ GAS je velmi jednoduch² proram. Nemß ╛ßdnou podporu
pro makra. Pokud ale pou╛ijete jako p°φponu .S
(ne .s
), pro╛ene se
nejprve soubor preprocesorem. Proto lze pou╛φt makra a konstanty
preprocesoru. DJGPP mß standardn∞ pom∞rn∞ ╣ikovnou sadu maker
v souboru libc/asmdefs.h
.
![]() |
![]() |
![]() |
|
![]() |
#include <libc/asmdefs.h> .file "jmeno.S" .data .align 2 mojedata: .word 0 ... .text .align 4 FUNC(__mojefunkce) ENTER movl ARG1, %eax ... jmp label ... label: ... LEAVE |
![]() |
|
![]() |
![]() |
![]() |
Takto napsanΘ funkce se potom chovßjφ jako standardnφ funkce v C. Prototyp vypadß asi takto:
![]() |
![]() |
![]() |
|
![]() |
void mojefunkce(int p); |
![]() |
|
![]() |
![]() |
![]() |
Volacφ konvence jsou standardnφ jako v C - parametry jsou na zßsobnφku obrßcen∞ (prvnφ pop vyzvedne prvnφ parametr). O vrßcenφ zßsobnφku se starß volajφcφ funkce. Pokud mßte k≤d napsan² pro pascal, tedy funkce po sob∞ uklφzφ samy, je nutnΘ k prototypu napsat:
![]() |
![]() |
![]() |
|
![]() |
void mojefunkce(int p) __attribute((stdcall)); |
![]() |
|
![]() |
![]() |
![]() |
GCC takΘ umφ registrovΘ volacφ konvence. Potom je prvnφch n
parametr∙ (maximßln∞ 3) ulo╛eno v registrech. To se deklaruje:
![]() |
![]() |
![]() |
|
![]() |
void mojefunkce(int a, int b, int c) __attribute((regparm(3))); |
![]() |
|
![]() |
![]() |
![]() |
Pou╛ijφ se registry EAX
, EDX
, ECX
. RegistrovΘ volacφ konvence
jsou Φasto rychlej╣φ, ne╛ zßsobnφkovΘ, proto takovΘ prototypy se
obΦas vyplatφ pou╛φt i u Φasto volan²ch cΘΦkov²ch funkci.
Nejd∙le╛it∞j╣φ pseudoinstrukce jsou:
.allign n
zarovnß adresu nßsledujφcφ instrukce tak, aby byla
d∞litelnß Φφslem n
.
.data
datov² segment
.text
k≤dov² segment
.globl symbol
symbol je globßlnφ
.comm symbol, dΘlka
nßsledujφcφ symbol je common (takov²ch symbol∙ m∙╛e b²t v objektech vφce se stejn²m jmΘnem a linker je potom spojφ za sebe.
.ascii
Ascii °et∞zec
.asciz
jako .ascii
ale p°idß nakonec nulu
.byte
8 bit∙
.short
16 bit∙
.int
32 bit∙
.quad
64 bit∙
.double
floating point hodnota
.float
floating point hodnota
.fill kolikrßt, velikost, hodnota
vypln∞nφ oblasti hodnotou
Nov∞j╣φ verze gas
u podoporujφ i v∞ci jako .ifdef
, .macro
atd. Zdß se
mi ale lep╣φ pou╛φt preprocesor. Vφce je v dokumentaci (info -f gas
)