Rezidentné programy


<= =>


Veľká skupina programov je schopná pracovať na pozadí prevádzanej úlohy. Patria medzi ne ovládače (myši, klávesnice, . . .), utility (hodiny, antivírová kontrola, sťahovače obrazoviek, . . .), vírusy (bez komentáru). Týmto programom pridávame označenie rezidentné.

Ich základnou vlastnosťou je ich neustála prítomnosť v pamäti počítača a schopnosť sa vyvolať, ak je to nutné. Z toho vyplývajú i požiadavky na ne: malá dĺžka kódu (musí obsadiť čo najmenej pamäti) a nezávislosť na spustených aplikáciách.

Činnosť týchto programov na pozadí aplikácií zaručuje ich volanie spolu s obsluhami prerušení. Ak teda dôjde k nejakej udalosti (stlačenie klávesy, prijatie dát na port, uplynutie určitej doby, . . .), je volané prerušenie obsluhujúce túto udalosť. Po tejto obsluhe (,alebo pred ňou) prebehne i časť rezidentného programu pripojeného k nej. Aby k tomu došlo, musí tvorca rezidentného programu zmeniť adresu v tabuľke vektorov prerušení na adresu svojho podprogramu. Pritom si starú adresu obsluhy uschová, aby mohol zaistiť volanie pôvodnej obsluhy udalosti. Je len na tvorcovi, či starú obsluhu bude volať alebo nie (ak ju ale nezavolá, môžu sa vyskytnúť problémy). Programátor sa tiež môže rozhodnúť, v ktorej časti svojho programu bude obsluhu volať (napr. nemôžem čítať aký kláves bol stlačený, keď ešte neprebehla obsluha klávesnice). Rezidentný program má tieto časti:

Musíme zaistiť správne alokovanie pamäte pre rezidentný program, (stack length, heap length) ktoré vymedzia oblasť rezervovanú pre zásobník atď. (hodnoty je najlepšie vyskúšať).

Najčastejšie sa pre rezidentné programy používajú prerušenia:

Ostatní hodnoty prerušení sa dajú zistiť z literatúry (alebo SYSMANa).

Na aké prerušenie rezident pripojíme, závisí do značnej miery na tom, čo má robiť a na čo má reagovať. Občas je dobré si v obsluhe jedného prerušenia nastaviť premenné a v závislosti na ich stave vykonať (alebo nevykonať) určitú činnosť v obsluhe iného prerušenia. Často si ani neuvedomíme, že náš podprogram pripojený k určitému prerušeniu, ho nepriamo volá. Dôjde tak k zacykleniu. Toho sa čiastočne vyvarujeme tým, že všetky činnosti, spojené so vstupmi a výstupmi, prevádzame sami a nevoláme “Céčkové” procedúry (napr. výstup na obrazovku realizujeme priamym zápisom do VRAM, použitie printf vedie k chybe).

#include <dos.h>

#ifdef __cplusplus
  #define __CPPARGS ...
#else
  #define __CPPARGS
#endif

// redukujeme veľkosť haldy a zásobníka, aby v pamäti zaberal menej miesta
extern unsigned _heaplen = 1024;   // veľkosť haldy (heap)
extern unsigned _stklen = 512;     // veľkosť zásobníka (stack)

void interrupt ( *oldhandler)(__CPPARGS);

void interrupt hodiny(__CPPARGS) {      // nová obsluha prerušenia
  asm {
    JMP zac          // preskoč dáta
    label vid dword
    DW 156,0xB800    // adresa miesta VRAM, kde budú hodiny
  }
  zac:
  asm MOV CL,2       // hodiny, minúty, sekundy (cy3lus)
  c1 :               // začiatok cyklu
  asm {
    LES BX,CS:[offset vid]  // naber adresu premennej slovo do BX
    XOR AH,AH        // vymaž hornú polovicu registra AX
    MOV AL,CL        // naber do dolnej polovice AX krok i
    SHL AL,1         // vynásob, AL:=AL*2
    SUB BX,AX        // odčítaj od BX obsah AX
    OUT 0x70,AL      // pošli na CMOS adresu čítanej slabiky
    SHL AL,1         // vynásob, AL:=AL*2
    SUB BX,AX        // odčítaj, to ovplyvní tvar výstupu
    IN AL,0x71       // prečítaj z CMOS obsah čítanej slabiky
    MOV DL,AL        // skopíruj obsah tejto slabiky do AH
    SHR DL,4         // desiatky posuň do dolnej polovice AH
    AND AX,0xF       // odstráň zbytočné bity
    AND DX,0xF
    OR AX,0x1F30     // urob prevod do ASCII, pridaj atr.
    OR DX,0x1F30
    MOV ES:2[BX],AX  // nastav jednotky vo VRAM
    MOV ES:[BX],DX   // nastav desiatky vo VRAM
    DEC CL           // znížiť CL
    JNS c1           // koniec cyklu
    MOV WORD PTR ES:[154],0x1F00+'.'   // vo VRAM oddeľ sekundy a minúty
    MOV WORD PTR ES:[148],0x1F00+'.'   // vo VRAM oddeľ minúty a hodiny
  }
  asm PUSHF
  oldhandler();      // zavolaj pôvodnú obsluhu
  asm POPF
}

int main(void) {
  oldhandler = getvect(0x1C);  // čítaj adresu súčasnej obsluhy
  setvect(0x1C, hodiny);       // na jej miesto daj adresu našej obsluhy
  // _psp je začiatočná adresa programu v pamäti. Vrchol zásobníka je koniec
  // programu. Použitím _SS a _SP spolu môžeme zistiť koniec zásobníka.
  keep(0, (_SS + (_SP/16) - _psp)); // ukonči s tým, že program zostane v pamäti
  return 0;
}

Uvedený program číta pri obsluhe prerušenia 0x1C stav hodín z pamäti CMOS. Po prepočte adries a úprave znakov z BCD kódu do ASCII je informácia o čase zobrazená v pravom hornom rohu obrazovky. Hlavný program má za úlohu len zmenu adresy pôvodnej obsluhy na našu.

Rezidentné programy


<= =>