Katkestused

Arvutid kasutavad katkestusi (interrups) mitmesugusteks ülesanneteks. Näiteks kui te vajutate mingile klahvile, saadab klaviatuuriprotsessor põhiprotsessorile ühe katkestuse. Katkestus on signaal, mis sunnib põhiprotsessorit oma tööd hetkeks katkestama ja täitma mingit muud ülesannet. Selleks salvestab protsessor oma hetkelise töö jätkamiseks vajalikud andmed ja asub seejärel sellele katkestusele vastavat ülesannet täitma. Mälu alguses, esimeses 1024 baidis asub tabel, mis sisaldab pikki viitasid iga katkestuse puhul täidetavale funktsioonile. Iga sissekanne sellesse tabelisse sisaldab 4 baiti (pika viida suurus). Seega sisaldab tabel täpselt 256 sissekannet. Kui te nüüd vajutasite näiteks mingile klahvile, siis katkestab protsessor oma hetkelise töö ja täidab sellele katkestusele (katkestus number 9) vastava ülesande. Selleks hangib ta katkestuste tabelist vastava (üheksanda) sissekande ja jätkab tööd sellelt aadressilt. Nimetatud aadressil asub tavaliselt operatsioonisüsteemi või BIOSi funktsioon, mis uurib järele, millisele klahvile vajutati ja väljastab vastava sümboli ekraanile. Peale selle funktsiooni täitmist jätkab protsessor oma endist tööd.

BIOS on kogumik masinkoodis funktsioone, mis täidavad mitmesuguseid lihtsaid ülesandeid nagu näiteks ekraanile väljastamine ja klaviatuurilt lugemine. BIOSi funktsioonid on salvestatud arvuti ROM (Read Only Memory) mälus. Sellises mälus salvestatut saab vaid lugeda, kuid mitte muuta. Seepärast ei ole arvutit startides vaja BIOSi mitte kettalt mällu lugeda, vaid ta juba on mälus. Arvuteid on väga mitmesuguseid ja seepärast loodi BIOS, mis kindlustab, et üks ja sama tarkvara saab töötada ilma muutusteta väga erinevatel arvutitel. Programm, mis soovib näiteks väljastada ekraanile mingit sümboli (tähe või numbri), salvestab vajalikud andmed sobivates registrites ja kutsub välja vastava BIOSi funktsiooni. Selleks peab ta aga kasutama sobivat katkestust. Nüüd täidab BIOSi funktsioon selle ülesande vastavalt antud arvuti ehitusele. Programm ei pea enam teadma, millist aadressi selles arvutis kasutatakse ühe või teise seadme jaoks, vaid ainult seda, milline katkestus seda tööd teeb ja milliseid parameetreid ta vajab. Need andmed on aga standardiseeritud ja nii saab sama programm töötada väga erinevatel arvutitel ilma nende ehitusest väga palju teadmata.

Katkestusi on väga mitmesuguseid. Osa neist on reserveeritud operatsioonisüsteemi jaoks. Need katkestused uuendavad näiteks arvuti kella seisu, värskendavad mälu (RAM refresh cycles), loevad mitmesuguste seadmete (klaviatuuri, hiire jt.) seisu ja väljastavad andmeid mitmesugustele seadmetele (ekraan, trükkal) jne.

Katkestused on jaotatud järgmistesse gruppidesse:

Mingi katkestuse kasutamiseks tuleb lihtsalt täita sobivad registrid vajalike väärtustega ja kutsuda nimetatud katkestus välja. Selleks sisaldab Borland C/C++ translaator funktsioone int86(), int86x(), intdos() ja intdosx().

int int86(int nIntNr, union REGS *inregs, union REGS *outregs);
int int86(int nIntNr, union REGS *inregs, union REGS *outregs, union SREGS *segregs);
int intdos(union REGS *inregs, union REGS *outregs);
int intdosx(union REGS *inregs, union REGS *outregs, union SREGS *segregs);

Kõik nimetatud funktsioonid kasutavad andmestruktuuri REGS:

union REGS {
 struct WORDREGS x;
 struct BYTEREGS h;
};

mis koosneb omakorda kahest andmestruktuurist:

struct BYTEREGS {
 unsigned char al, ah, bl, bh;
 unsigned char cl, ch, dl, dh;
};

ja

struct WORDREGS {
 unsigned int ax, bx, cx, dx;
 unsigned int si, di, cflag, flags;
};

Nimetatud funktsioonid ja andmestruktuurid on defineeritud päisefailis DOS.H. Need andmestruktuurid jäljendavad protsessori registrite struktuuri. Programm, mis soovib mingit katkestust kasutada, defineerib oma andmesegmendis kaks muutujat tüübist REGS. Ühe neist täidab ta antud katkestuse jaoks sobivate väärtustega. Seejärel kutsub ta välja näiteks funktsiooni int86(). Funktsiooni esimene parameeter määrab, millise järjekorranumbriga katkestust välja kutsuda. Teine ja kolmas parameeter sisaldavad viitasid REGS tüüpi andmestruktuuridele. Esimese andmestruktuuri sisu kopeeritakse enne katkestuse kasutamist protsessori registritesse. Teine struktuur täidetakse peale katkestust protsessori registrites leitud väärtustega. Need väärtused määravad katkestuse tulemuse. Funktsiooni int86() viimased kaks parameetrit võivad osutada ühele ja samale andmestruktuurile. Sel juhul kirjutatakse funktsioonile üleantud struktuuri sisu üle uute väärtustega. Üleantud väärtuste säilitamine ei ole aga tavaliselt vajalik ja see aitab mälu kokku hoida.

Kuna võtmesõna union tähendab, et need struktuurid kasutavad üht ja sama mälupiirkonda, siis võite sobivat struktuuri kasutades muuta nii tervet registrit AX (või muid üldisi registreid) kui ka tema osasid AL ja AH.

Struktuur WORDREGS sisaldab veel elementi cflags, mis ei vasta ühelegi protsessori registrile. Protsessori register flags (tähised) sisaldab mitmeid ühebitised tähiseid. Üks neist - CF (carry flag) märgib katkestuste puhul tavaliselt viga. Selle tähise väärtus on ka veel eraldi salvestatud nimetatud elemendis cflag ja see on ka funktsiooni enda väärtuseks. Niisiis, kui funktsiooni väärtus (või tema struktuuri WORDREGS elemendi cflag väärtus) on nullist erinev, siis on tekkinud mingi viga. Päisefailis DOS.H on defineeritud ka globaalne muutuja _doserrno, mille väärtus määrab konkreetse vea. See muutuja on täisarv, mis võib omada päisefailis DOS.H defineeritud väärtusi.

Funktsioon int86x() vajab veel neljandat parameetrit, mis osutab andmestruktuurile SREGS:

struct SREGS {
 unsigned es;
 unsigned cs;
 unsigned ss;
 unsigned ds;
};

Selle parameetri abil võib muuta registrite DS, ES sisu ka enne katkestuse väljakutsumist. Funktsioon int86() seevastu kasutab programmi oma DS väärtust.

Funktsioonid intdos() ja intdosx() ei vaja katkestuse numbrit, kuna nad kasutavad vaid üht kindlat katkestust - 21hex, mis on ette nähtud operatsioonisüsteemi DOS teenuste kasutamiseks.

Järgmises näiteprogrammis näete, kuidas nimetatud funktsioone kasutada. See fail sisaldab funktsioone hiire kasutamiseks graafilistes ja tekstirezhiimis töötavates programmides. Siindefineeritud funktsioone kasutame järgmiste peatükkide näiteprogrammides. Lisatud on ka lihtne näiteprogramm MOUDEMO, mis kasutab osa neist funktsioonidest. Neid funktsioone saab kasutada ka tekstirezhiimis töötavates programmides. Hiirekursori koordinaadid antakse aga ka tekstirezhiimis pikselitena. Seega tuleb neid tekstirezhiimis jagada vastavalt ekraani lahutusvõimele sobiva arvuga (tavaliselt 8). Tulemuse täisarvuline osa määrab sobiva koordinaadi tekstirezhiimis.


MOUFUNC.H

/*********************************************************/
/***                                                   ***/
/***   MouFunc.H                                       ***/
/***                                                   ***/
/***   See fail sisaldab sümboolseid konstante ja      ***/
/***   funktsioonide deklaratsioone faili MOUFUNC.C    ***/
/***   jaoks.                                          ***/
/*********************************************************/

#include <dos.h>

#define MOU_NBUTTON		0
#define MOU_LBUTTON		1
#define MOU_RBUTTON		2
#define MOU_BBUTTON		3
#define MOU_GETLBUTTONINFO    0
#define MOU_GETRBUTTONINFO    1

#ifndef __MOUFUNC_C_

extern int MouInit( void );
extern void MouShow( void );
extern void MouHide( void );
extern int MouGetInfo(int *, int *);
extern int MouGetPressed(int, int*, int*);
extern int MouGetReleased(int, int*, int*);
extern void MouGotoXY(int, int);
extern void MouSetAlNumCursor(int, char, char, char, char);

#endif

MOUFUNC.C

/*******************************************************/
/***                                                 ***/
/***   MouFunc.C                                     ***/
/***                                                 ***/
/***   See fail sisaldab funktsioone hiire kasuta-   ***/
/***   miseks. Kui te soovite neid funktsioone oma   ***/
/***   programmis kasutada, siis tuleb teil lisada   ***/
/***   oma programmi päisefail MOUFUNC.H.            ***/
/***   Lisaks sellele tuleb teil installeerida mingi ***/
/***   standardne hiire ohjurprogramm. Siintoodud    ***/
/***   funktsioonid kasutavad vaid ohjurprogrammi    ***/
/***   teenuseid.                                    ***/
/*******************************************************/

#define  __MOUFUNC_C_
#include  "moufunc.h"

/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouInit()                                     ---*/
/*---                                                 ---*/
/*---   See funktsiooni initsialiseerib hiire         ---*/
/*---   ohjurprogrammi. Funktsiooni väärtus on nullist---*/
/*---   erinev, kui initsialiseerimine onnestus,      ---*/
/*---   vastasel juhul null.                          ---*/
/*-------------------------------------------------------*/
int MouInit( void )
{
  union REGS  myregs;
  myregs.x.ax = 0;		   /* katkestuse nr 33 funktsioon 0 */
  int86(0x33, &myregs, &myregs);   /* algväärtus ei
                                              oma tähendust */
  return myregs.x.ax;              /* tulemus asub registris AX */
}	/* MouInit */


/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouShow()                                     ---*/
/*---                                                 ---*/
/*---   See funktsioon kuvab hiirekursori ekraanile.  ---*/
/*---   Enne kui te midagi ekraanile esitate voi      ---*/
/*---   mingit ekraani osa rasterpildina salvestate,  ---*/
/*---   oleks mottekas hiirekursor ekraanilt eemal-   ---*/
/*---   dada ja peale seda uuesti ekraanile esitada.  ---*/
/*---   Hiirekursori ekraanilt eemaldamiseks          ---*/
/*---   kasutage funktsiooni MouHide().               ---*/
/*-------------------------------------------------------*/
void MouShow( void )
{
  union REGS  myregs;
  myregs.x.ax = 1;		   /* katkestuse nr 33 funktsioon 1 */
  int86(0x33, &myregs, &myregs);
}	/* MouShow */


/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouHide()                                     ---*/
/*---                                                 ---*/
/*---   Eemaldab hiirekursori ekraanilt.              ---*/
/*-------------------------------------------------------*/
void MouHide( void )
{
  union REGS  myregs;
  myregs.x.ax = 2;		   /* katkestuse nr 33 funktsioon 2 */
  int86(0x33, &myregs, &myregs);
}	/* MouHide */


/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouGetInfo()                                  ---*/
/*---                                                 ---*/
/*---   See funktsioon hangib hiirekursori hetkelise  ---*/
/*---   positsiooni ekraanil ja uurib, kas mingi      ---*/
/*---   hiireklahv on alla vajutatud. Funktsiooni     ---*/
/*---   tulemuseks on konstant, mis määrab, milline   ---*/
/*---   hiireklahv (kui ükski) on alla vajutatud.     ---*/
/*---   See voib olla üks järgmistest väärtustest:   ---*/
/*---   MOU_NBUTTON (0) - ükski klahv ei ole all.     ---*/
/*---   MOU_LBUTTON (1) - vasak klahv on all          ---*/
/*---   MOU_RBUTTON (2) - parempoolne klahv on all.   ---*/
/*---   MOU_BBUTTON (3) - molemad klahvid on all.     ---*/
/*---   Viimane väärtus vastab kolmeklahvilise hiire  ---*/
/*---   puhul keskmisele klahvile.                    ---*/
/*-------------------------------------------------------*/
int MouGetInfo(int *pRow, int *pColumn)
{
  union REGS  myregs;
  myregs.x.ax = 3;		   /* katkestuse nr 33 funktsioon 3 */

  int86(0x33, &myregs, &myregs);
  *pRow = myregs.x.dx;
  *pColumn = myregs.x.cx;
  return myregs.x.bx;
}	/* MouGetInfo */


/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouGetPressed()                               ---*/
/*---                                                 ---*/
/*---   See funktsioon uurib, kui mitu korda soovitud ---*/
/*---   hiireklahvi on vajutatud alates sellest       ---*/
/*---   hetkest, kui seda funktsiooni viimati         ---*/
/*---   kasutati ja millises punktis teda viimati     ---*/
/*---   vajutati. Kui funktsiooni esimene parameeter  ---*/
/*---   on null, siis uuritakse vasaku klahvi andmeid,---*/
/*---   kui aga 1, siis parema klahvi andmeid.        ---*/
/*-------------------------------------------------------*/
int MouGetPressed(int nButton, int *pRow, int *pColumn)
{
  union REGS  myregs;

  myregs.x.ax = 5;		   /* katkestuse nr 33 funktsioon 5 */
  myregs.x.bx = nButton;	   /* milline klahv meid huvitab */
  int86(0x33, &myregs, &myregs);
  *pRow = myregs.x.dx;
  *pColumn = myregs.x.cx;
  return myregs.x.bx;		   /* kui mitu korda seda klahvi */
                                   /* on vajutatud */
}	/* MouGetPressed */


/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouGetReleased()                              ---*/
/*---                                                 ---*/
/*---   See funktsioon uurib, kui mitu korda soovitud ---*/
/*---   hiireklahvi on vabastatud alates sellest, kui ---*/
/*---   seda funktsiooni viimati kasutati ja millises ---*/
/*---   punktis ta viimati vabastati.                 ---*/
/*-------------------------------------------------------*/
int MouGetReleased(int nButton, int *pRow, int *pColumn)
{
  union REGS  myregs;
  myregs.x.ax = 6;		   /* katkestuse nr 33 funktsioon 4 */
  myregs.x.bx = nButton;	   /* milline klahv meid huvitab */
  int86(0x33, &myregs, &myregs);
  *pRow = myregs.x.dx;
  *pColumn = myregs.x.cx;
  return myregs.x.bx;		   /* kui mitu korda seda klahvi */
                                   /* on lahti lastud */
}	/* MouGetReleased */
 

/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouGotoXY()                                   ---*/
/*---                                                 ---*/
/*---   Nihutab hiirekursori soovitud punkti.         ---*/
/*-------------------------------------------------------*/
void MouGotoXY(int nRow, int nColumn)
{
  union REGS  myregs;
  myregs.x.ax = 4;		   /* katkestuse nr 33 funktsioon 4 */
  myregs.x.dx = nRow;
  myregs.x.cx = nColumn;
  int86(0x33, &myregs, &myregs);
}	/* MouGotoXY */
 

/*-------------------------------------------------------*/
/*---                                                 ---*/
/*---   MouSetAlNumCursor()                           ---*/
/*---                                                 ---*/
/*---   See funktsioon määrab hiirekursori kuju ja    ---*/
/*---   välimuse, kui ekraan on tekstirezhiimis.      ---*/
/*---   Parameetrid nScreenChar ja nScreenAttr        ---*/
/*---   määravad, kui palju hiirekursori alla jäänud  ---*/
/*---   tähest peab jääma nähtavaks. Väärtus 0xFF     ---*/
/*---   jätab kogu tähe muutumatuks, väärtus 0       ---*/
/*---   kustutab ta täielikult. Nimetatud parameetrid ---*/
/*---   liidetakse loogilise AND operatsiooniga ja    ---*/
/*---   saadud tulemusele liidetakse parameerite      ---*/
/*---   nCursorChar ja nCursorAttr väärtused loogilise---*/
/*---   XOR operatsiooniga.                           ---*/
/*-------------------------------------------------------*/
void MouSetAlNumCursor(int fSoftware, char nScreenChar, char nScreenAttr,
					char nCursorChar, char nCursorAttr)
{
  union REGS  myregs;
  myregs.x.ax = 10;		   /* katkestuse nr 33 funktsioon 10 */
  myregs.x.bx = fSoftware;
  myregs.h.cl = nScreenChar;
  myregs.h.ch = nScreenAttr;
  myregs.h.dl = nCurorChar;
  myregs.h.dh = nCursorAttr;
  int86(0x33, &myregs, &myregs);
}	/* MouSetAlNumCursor */

MOUDEMO.C

/*********************************************************/
/***                                                   ***/
/***   MouDemo.C                                       ***/
/***                                                   ***/
/***   See fail demonstreerib mitmete failis MOUFUNC.C ***/
/***   defineeritud funktsioonide toimet.              ***/
/*********************************************************/

#include <graphics.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include "moufunc.h"

/*=======================================================*/
/*===                                                 ===*/
/*===   Main()                                        ===*/
/*===                                                 ===*/
/*===   Initsialiseerib graafilise rezhiimi ja        ===*/
/*===   proovib mitmeid failis MOUFUNC.C defineeritud ===*/
/*===   funktsioone. Seejärel proovitakse neid ka     ===*/
/*===   tekstrezhiimis.                               ===*/
/*=======================================================*/
int main( void )
{
  int 	 gdriver = DETECT, gmode, errorcode;
  int	 x, y, button;
  char   buf[30];
  int 	 coord[8];

  /* korrigeeride seda BGI ohjurprogrammide kataloogi kui vaja */
  initgraph(&gdriver, &gmode, "C:\\BORLANDC\\BGI");
  errorcode = graphresult();

  if (errorcode != grOk)
  {
     printf("Graafika viga: %s\n", grapherrormsg(errorcode));
     printf("Vajutage suvalisele klahvile:");
     getch();
     exit(1);
  }

  if(!MouInit()) {
    printf("Ei suutnud hiire ohjurprogrammi initsialiseerida!\n");
    printf("Vajutage suvalisele klahvile:");
    getch();
    exit(1);
  }

  outtextxy(30, getmaxy() - 30, "Vajutage suvalisele klahvile programmi lopetamiseks");
  MouShow();       /* esita hiirkursor ekraanile */
  setfillstyle(SOLID_FILL, 0);
  coord[0] = 100;              /* see ristkülik on vaja täita */
  coord[1] = 200;              /* tagaplaani värviga iga kord */
  coord[2] = getmaxx() - 50;   /* enne uue teksti väjastamist,*/
  coord[3] = coord[1];         /* selleks et puhastada ekraan */
  coord[4] = coord[2];
  coord[5] = 250;
  coord[6] = coord[0];
  coord[7] = coord[5];

  do {
    button = MouGetInfo(&y, &x);
    fillpoly(4, coord);
    MouHide(); /* varja hiirekursor enne teksti vms väljastamist */
    switch(button) {
      case MOU_NBUTTON:
        sprintf(buf, "Hiirekursor asub punktis: X = %d, Y = %d", x, y);
	break;
      case MOU_LBUTTON:
	sprintf(buf, "Hiire vasakut klahvi vajutati punktis: X = %d,Y = %d", x, y);
	break;
      case MOU_RBUTTON:
	sprintf(buf, "Hiire paremat klahvi vajutati punktis: X = %d,Y = %d", x, y);
	break;
      case MOU_BBUTTON:
	sprintf(buf, "Hiire molemat klahvi vajutati punktis: X = %d,Y = %d", x, y);
	break;
    };

    outtextxy(110, getmaxy() / 2, buf);
    MouShow();  /* nüüd kuva uuesti hiirekursor ekraanile */
  } while(!kbhit());

  closegraph();
  exit(0);
  return 0;
}	/* main */