Jak se pověsit (na interrupt) -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- In the beginning there was data. The data was without form and null, and darkness was upon the face of the console; and the Spirit of IBM was moving over the face of the market. And DEC said, "Let there be registers"; and there were registers. And DEC saw that they carried; and DEC separated the data from the instructions. DEC called the data Stack, and the instructions they called Code. And there was evening and there was morning, one interrupt. -- Rico Tudor, "The Story of Creation or, The Myth of Urk" 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 zpravujete) a z něho se vyvolá další (výpadek stránkování), procesor spanikaří a vyvolá se automaticky interrupt "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 (zamknout ho). 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ě zavařit. Je možné také zamykat jednodlivé části 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 přes 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 zamkne oblast mezi funkcí jmeno a funkcí xjmeno_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 starat 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); Taky 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 zdrojáků. 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 pcspeakeru (40000 interruptů do sekundy). Například ale pod Qemmin DPMI to už nestihne. výheň