P°echßzφme na DJGPP


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.

32 bit∙

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.

Prefixy

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 shortlong 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.

Struktury a data na disku

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.

Adresace

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 farnear, 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 farnear 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 farnear 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 a virtußlnφ pam∞╗

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.

VCPI, DPMI, V86 a dal╣φ

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 clisti. 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).

CWSDPMI a PMODE

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.

Stub

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.

Emulace koprocesoru

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

Zavßd∞cφ k≤d a velikost .EXE souboru

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 .exeargv[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ß:

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 argvargc 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.

P°φstup k fyzickΘ pam∞ti

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*_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_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.

Volßnφ interrupt∙ a p°φstup na porty

Obojφ je velmi snadnΘ. O p°φstup na porty se starajφ inlinovanΘ funkce outport*(port,hodnota)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);
        }

Jak se pov∞sit (na interrupt)

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_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.

Assembler

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.

Pseudoinstrukce pro urΦovßnφ segnetu

.data

datov² segment

.text

k≤dov² segment

Scope

.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.

Pseudoinstrokce pro zadßvßnφ dat

.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 gasu podoporujφ i v∞ci jako .ifdef, .macro atd. Zdß se mi ale lep╣φ pou╛φt preprocesor. Vφce je v dokumentaci (info -f gas)