Učebnice Assembleru 86

Rezidentní programy

Velká skupina programů je schopna pracovat na pozadí prováděné úlohy. Patří mezi ně ovladače (myši, klávesnice, . . .), utility (hodiny, antivirová kontrola, stahovače obrazovek, . . .), viry (bez komentáře). Těmto programům přidáváme označení rezidentní.

Jejich základní vlastností je jejich neustálá přítomnost v paměti počítače a schopnost se vyvolat, jestliže je to nutné. Z toho vyplývají i požadavky na ně: malá délka kódu (musí obsadit co nejméně paměti) a nezávislost na spuštěných aplikacích.

Činnost těchto programů na pozadí aplikací zaručuje jejich volání spolu s obsluhami přerušení. Jestliže tedy dojde k nějaké události (stisk klávesy, přijetí dat na port, uplynutí určité doby, . . .), je voláno přerušení obsluhující tuto událost. Po této obsluze (,nebo před ní) proběhne i část rezidentního programu připojeného k ní. Aby k tomu došlo, musí tvůrce rezidentního programu změnit adresu v tabulce vektorů přerušení na adresu svého podprogramu. Přitom si starou adresu obsluhy uschová, aby mohl zajistit volání původní obsluhy události. Je jen na tvůrci, jestli starou obsluhu bude volat nebo ne (jestliže ji ale nezavolá, mohou se vyskytnout problémy). Programátor se také může rozhodnout, ve které části svého programu bude obsluhu volat (např. nemohu číst jaká klávesa byla stisknuta, když ještě neproběhla obsluha klávesnice). Rezidentní program má tyto části:

  • podprogramy, které jsou volány s přerušením (za jejich hlavičkou následuje slovo interrupt) vykonávající užitečnou nebo záškodnickou činnost; ty navíc mohou volat původní obsluhy (posloupností instrukcí PUSHF, CALL adresa staré obsluhy)
  • hlavní program, který má za úkol:
    • přečtení adresy původní obsluhy přerušení a její uložení do proměnné (typu procedure); to zajistí procedura z knihovny DOS: GetIntVec (číslo přerušení, adresa proměnné typu procedure)
    • změna původní adresy na adresu našeho podprogramu; to zajistí procedura z knihovny DOS: SetIntVec (číslo přerušení, adresa našeho podprogramu)
    • ohlášení instalace (např. Writeln ('Rezidentní program instalován.');)
    • ukončení programu s tím, že zůstane v paměti; to zajistí procedura Keep (0)

V Pascalu musíme navíc v rezidentním programu ohraničit podprogramy interrupt direktivou {$F+}, která zajistí, že bude uvnitř použito vzdálené volání (za podprogram napíšeme {$F-} pro návrat do automatického zjišťování vzdálených adres). Navíc musíme zajistit správnou alokaci paměti pro rezidentní program označením v úvodu programu {$M 400,0,0}, které vymezí oblast rezervovanou pro zásobník atd. (hodnoty je nejlepší vyzkoušet).

Nejčastěji se pro rezidentní programy používají přerušení:

  • $1C - volané 18,2krát za vteřinu
  • $09 - volané po události na klávesnici (stisk klávesy)
  • $28 - volané v případě, že mikroprocesor není zatížený (čeká . . .)

Ostatní hodnoty přerušení se dají zjistit z literatury (nebo SYSMANu).

Na jaké přerušení rezident připojíme, závisí do značné míry na tom, co má dělat a na co má reagovat. Občas je dobré si v obsluze jednoho přerušení nastavit proměnné a v závislosti na jejich stavu vykonat (nebo nevykonat) určitou činnost v obsluze jiného přerušení. Často si ani neuvědomíme, že náš podprogram připojený k určitému přerušení, ho nepřímo volá. Dojde tak k zacyklení. Toho se částečně vyvarujeme tím, že veškeré činnosti, spojené se vstupy a výstupy, provádíme sami a nevoláme pascalovské procedury (např. výstup na obrazovku realizujeme přímým zápisem do VRAM, použití writeln vede k chybě).

Příklad:

{$M $400,0,0}           {nastav paměť: zásobník $400 slabik}
uses Dos;
var IntVec : Procedure; {proměnná pro adresu staré obsluhy}

{$F+}                   {vzdálená volání}
procedure hodiny;interrupt;assembler; {nová obsluha přerušení}
asm
 JMP @zac               {přeskoč data}
@vid:
 DW 156,$B800           {adresa místa VRAM, kde budou hodiny}
@zac:
 MOV CL,2               {hodiny, minuty, vteřiny (cyklus)}
@c1 :                   {začátek cyklu}
 LES BX,CS:[OFFSET @vid]{naber adresu proměnné slovo do BX}
 XOR AH,AH              {vymaž horní polovinu registru AX}
 MOV AL,CL              {naber do dolní poloviny AX krok i}
 SHL AL,1               {vynásob, AL:=AL*2}
 SUB BX,AX              {odečti od BX obsah AX}
 OUT $70,AL             {pošli na CMOS adresu čtené slabiky}
 SHL AL,1               {vynásob, AL:=AL*2}
 SUB BX,AX              {odečti, to ovlivní tvaru výstupu}
 IN AL,$71              {přečti z CMOS obsah čtené slabiky} 
 MOV DL,AL              {zkopíruj obsah této slabiky do AH}
 SHR DL,4               {desítky posuň do dolní poloviny AH}
 AND AX,$F              {odstraň zbytečné bity}
 AND DX,$F
 OR AX,$1F30            {proveď převod do ASCII, přidej atr.}
 OR DX,$1F30 MOV ES:2[BX],AX {nastav jednotky ve VRAM}
 MOV ES:[BX],DX         {nastav desítky ve VRAM}
 DEC CL                 {snížit CL}
 JNS @c1                {konec cyklu}
 MOV WORD PTR ES:[154],$1F00+'.'{ve VRAM odděl vteřiny a minuty}
 MOV WORD PTR ES:[148],$1F00+'.'{ve VRAM odděl minuty a hodiny}
 PUSHF                  {do zásobníku registr příznaků}
 CALL IntVec            {volej starou obsluhu $1C}
end;
{$F-}                   {konec vzdálených volání}

begin                        {hlavní program}
 GetIntVec($1c,@IntVec);     {čti adresu staré obsluhy}
 SetIntVec($1c,Addr(hodiny));{na její místo dej adresu mojí obsluhy}
 Writeln('Rezidentní hodiny instalovány.');{informuj o instalaci}
 Keep (0);                   {ukonči s tím, že zůstane program v paměti}
end.

Uvedený program čte při obsluze přerušení $1C stav hodin z paměti CMOS. Po přepočtu adres a úpravě znaků z BCD kódu do ASCII je informace o čase zobrazena v pravém horním rohu obrazovky. Hlavní program má za úkol jen změnu adresy původní obsluhy na naší.

Směr