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ň