Was sind eigentlich Interrupte?


Interrupte, genauer gesagt BIOS- und API-Routinen-Aufrufe, ermöglichen auch unter QuickBasic so ziemlich alles wofür man sonst C++ oder Turbopascal bräuchte. Diesen Bereich habe ich als Fragen-Antwort-Komplex aufgebaut. Es sind genau die Fragen die bisher an mich gerichtet wurden:

Was sind eigentlich Interrupte?
Wozu brauche ich eigentlich Interrupte?
Was ist nötig um Interrupte aufzurufen?
Kann man auch in QBasic Interrupte aufrufen?
Wie rufe ich denn nun einen Interrupt auf?
Was für Register gibt es und wofür ist welches gut?
Was für Interrupte bzw. Funktionen gibt's denn nun alles?


Frage:  Was sind eigentlich Interrupte?
Antwort:  Am Anfang gleich die größte Verwirrung. Der Begriff "Interrupt" ist nämlich eigentlich falsch gewählt an dieser Stelle. Vielmehr geht es um Routinen und Unterprogramme die schon vom Betriebssystem bereitgestellt werden. Diese Routinen gibt es sowohl im BIOS als auch im API (Aplication Program Interface) und das schon seit der ersten DOS Version. Der große Vorteil dieser Routinen ist, daß sie in jedem DOS basierenden Rechner (also auch in Windows 9x / ME / XP und weiß der Geier) vorhanden und vor allem kompatibel sind.

nach oben

Frage: Wozu brauche ich eigentlich Interrupte?
Antwort: Es gibt so manche Sache die sich nicht direkt mit QBasic bzw. QuickBasic lösen läßt. Das sind zum Beispiel Mausfunktionen, das Schreiben und Lesen von Datenträgern auf Sektorbasis, Einstellen von Videomodi's, das Arbeiten mit CD-ROM und vieles mehr. Man könnte sich statt der Interrupte natürlich auch entsprechende Librarys downloaden und einbinden, aber der Gebrauch von Interrupten hat einige Vorteile:

Kompatibilität - der Einsatz von Interrupten garantiert gleiche Funktionen bei unterschiedlichen Betriebssystemen.
Flexibilität - d.h. höhere Geschwindigkeit der Programme.
Sicherheit des Quellcodes - das Programm stürzt nicht ab weil in irgend einer Library ein Bug sitzt.
Entwerfen eigener angepaßter Library's.
Bessere Ausnutzung von Speicherplatz, da nur das im Quellcode steht was auch reingehört.
UND - wir wollen ja programmieren - nicht zusammenkleistern.


nach oben

Frage: Was ist nötig um Interrupte aufzurufen?
Antwort: Alles was dazu nötig ist, ist die Einbindung der Standardbibliothek von QuickBasic (QB.QLB). Das geht fast von allein indem man die QuickBasic-EXE mit der Option /L aufruft. (Voraussetzung ist natürlich, daß die Datei im selben Verzeichnis wie die QuickBasic-EXE ist, oder daß der Suchpfad richtig gesetzt wurde.) Die Zeile hat dann z.B. diesen Syntax C:\QB\QB.EXE /L C:\QB\QB.QLB
Des weiteren muß in QuickBasic (also im Programm) die Bibliotheksdatei eingebunden werden. Dies geschieht mit der Zeile: REM $INCLUDE:'QB.BI' Diese Bibliotheksdatei enthält alle Type-Definitionen die die neuen Befehle benötigen.

Ist alles erledigt hat man 5 neue Befehle:
CALL INTERRUPT  - ermöglicht den Aufruf von Interrupten
CALL INTERRUPTX - wie oben, jedoch mit Übergabe der Segmentregister
CALL ABSOLUTE   - Start von Maschinenprogrammen
CALL INT86OLD   - Kompatibilitätswahrung (wie INTERRUPT)
CALL INTX86OLD  - Kompatibilitätswahrung (wie INTERRUPTX)

Ich habe festgestellt, daß man eigentlich nur einen einzigen dieser Befehle tatsächlich braucht: CALL INTERRUPT. Da dieser aber (eigentlich) die Übergabe der sogenannten Segmentregister nicht unterstützt, habe ich die Datei QB.BI kurzerhand etwas umgeschrieben. So brauche ich nur einen einzigen Befehl der alles kann. Die "neue" Include Datei trägt bei mir den Namen: INTERUPT.BI und befindet sich (natürlich) im INCLUDE Verzeichnis - damit sie QuickBasic auch findet. Da ich diese Datei zum Teil auch in meinen Programmbeispielen verwende, findest du sie dort immer mal wieder. Wo das der Fall ist, mache ich aber darauf aufmerksam.

Und so sieht die Datei aus:
REM ***** Anfang Datei: INTERUPT.BI *****
   TYPE RegType
      ax AS INTEGER
      bx AS INTEGER
      cx AS INTEGER
      dx AS INTEGER
      bp AS INTEGER
      si AS INTEGER
      di AS INTEGER
      flags AS INTEGER
      ds AS INTEGER
      es AS INTEGER
   END TYPE
DECLARE SUB INTERRUPT (intnum AS INTEGER, inreg AS RegType, outreg AS RegType)
DIM SHARED Reg AS RegType
REM ***** Ende Datei *****


nach oben

Frage:  Kann man auch in QBasic Interrupte aufrufen?
Antwort:  Aus „Altertumsgründen“ gibt es in Qbasic die Funktionen zum Aufrufen von Interrupten nicht. Das Ausführen von Assemblerprogrammen, also Maschinenunterprogrammen, über CALL ABSOLUTE ist jedoch möglich. Dadurch und durch ein kleines Hilfsprogramm wird es möglich Interrupte auch unter Qbasic zu nutzen. Download

nach oben

Frage:  Wie rufe ich denn nun einen Interrupt auf?
Antwort: 

 

Als Beispiel betrachten wir eine der Mausfunktionen. Die nachfolgende Beschreibung benötigen wir zu jeder Funktion die wir aufrufen wollen.
Funktion: Interrupt 33h - Funktion 01h des DOS-API
Beschreibung: Maus-Cursor auf dem Bildschirm anzeigen. Nach dem Aufruf dieser Funktion wird der Maus-Cursor auf dem Bildschirm sichtbar und folgt fortan den Bewegungen der Maus über den Bildschirm.
Eingabe: AX = 0001h
Ausgabe: keine

Mit dem Aufruf dieser Funktion ist die Inkrementierung (Erhöhung) eines internen Zählers verbunden, der darüber entscheidet, ob der Maus-Cursor auf dem Bildschirm dargestellt wird. Enthält dieser Zähler den Wert 0, wird der Maus-Cursor auf dem Bildschirm dargestellt, während der Wert -1 seine Ausblendung zur Folge hat. Beim Aufruf der Funktion 00h (Reset) wird dieser Zähler zunächst auf -1 gesetzt, um dann durch den ersten Aufruf dieser Funktion den Wert 0 anzunehmen und wieder auf dem Bildschirm zu erscheinen. Der Maustreiber verfolgt die Mausbewegung auch, wenn der Maus-Cursor nicht auf dem Bildschirm dargestellt wird, so daß der Maus-Cursor nach dem Aufruf dieser Funktion nicht unbedingt wieder an der Stelle erscheinen muß, an der er sich bei seiner Ausblendung befand.

Hinweis: Unter MS-DOS muß ein Maustreiber installiert sein.

Und nun ein Programmbeispiel zu dem Ganzen:

REM ***** Beispielprogramm: Interruptaufruf *****
  
REM $INCLUDE:'INTERUPT.BI'
   CLS
   PRINT "Bewege die Maus"
   Reg.ax = &h1
   CALL INTERRUPT(&h33, Reg, Reg)
   WHILE INKEY$ = "": WEND
REM ***** Programmende *****

Was haben wir gerade gemacht?

Zeile 1: Wir binden die INCLUDE-Datei ein. (kann auch QB.BI sein)
Zeile 2: Bildschirm löschen
Zeile 3: Bildschirmhinweis
Zeile 4: Laden des Registers AX mit 01h (Funktionsnummer s.o.)
Zeile 5: Aufruf des Interrupts (33h ist die Interruptnummer)
Zeile 6: Auf eine Taste warten

Und das war's schon - wir haben unseren ersten Interrupt aufgerufen.

Das Prinzip ist immer gleich. Die entsprechenden Register werden geladen und der entsprechende Interrupt wird aufgerufen. Ist doch eigentlich ganz easy - und dafür 'ne ganze Library runterladen?


nach oben

Frage: Was für Register gibt es und wofür ist welches gut?
Antwort: Für den Aufruf von DOS- und BIOS-Funktionen sind vor allem die allgemeinen Register von Bedeutung, denn mit ihrer Hilfe werden der jeweiligen Funktion die Parameter übergeben, die sie zu ihrer Ausführung benötigt. Hauptsächlich diese Register sind es auch, die durch arithmetische Operationen (Addition, Subtraktion etc.) beeinflußt werden, die auf der Prozessorebene den Mittelpunkt aller Softwareaktivitäten darstellen. Eine Sonderstellung innerhalb des Registersatzes kommt dabei den Registern AX, BX, CX und DX zu, weil sie jeweils in zwei 8-Bit-Register unterteilt werden können. Damit besteht jedes dieser Register praktisch aus drei verschiedenen Registern: einem 16 Bit großen und zwei 8 Bits kleinen. Diese "kleinen" Register werden jeweils mit H (high) und mit L (low) bezeichnet, wodurch sich z.B. das 16 Bits umfassende AX-Register in die 8- Bit-Register AH und AL aufspaltet. Die Anordnung des H- und des L-Registers ist so gewählt, daß das L-Register die niederwertigen 8 Bits (Bits 0 bis 7) und das H-Register die höherwertigen 8 Bits (Bits 8 bis 15) des X-Registers belegt. Das AH-Register setzt sich damit aus den Bits 8 bis 15 und das AL-Register aus den Bits 0 bis 7 des AX-Registers zusammen.

Das Flag-Register dient zunächst einmal zur Kommunikation zwischen aufeinanderfolgenden Maschinensprache-Befehlen, indem es den Status arithmetischer und logischer Operationen speichert. So kann ein Programm nach der Addition zweier 16-Bit-Register anhand des Carry-Flags z.B. feststellen, ob das Resultat größer als 65535 war und deshalb nicht mehr als 16-Bit-Zahl dargestellt werden konnte. Ähnliche Aufgaben übernehmen auch das Sign-, das Zero- und das Overflow-Bit, mit deren Hilfe sich nach dem Vergleich zweier Register feststellen läßt, ob der Wert des ersten Registers größer, kleiner oder gleich dem des zweiten war.
Für die Systemprogrammierung auf Hochsprachen-Ebene ist lediglich das Carry-Flag und in ganz wenigen Ausnahmen auch das Zero-Flag von Bedeutung, denn mit Hilfe dieser Flags zeigen die meisten DOS- und BIOS-Funktionen ihrem Aufrufer an, ob während ihrer Ausführung ein Fehler aufgetreten ist, weil beispielsweise nicht genügend Speicher zur Verfügung stand, ein unbekannter Dateiname angegeben wurde oder was auch immer.

Das Flag-Register:

   Bit  0 - CF = Carry Flag
   Bit  2 - PF = Parity Flag
   Bit  4 - AF = Auxiliary Flag
   Bit  6 - ZF = Zero Flag
   Bit  7 - SF = Sign Flag
   Bit  8 - TF = Trap Flag
   Bit  9 - IF = Interrupt Flag
   Bit 10 - DF = Direction Flag
   Bit 11 - OF = Overflow Flag

Bleiben nur noch die Segment-Register. (CS, DS, SS und ES) diese finden immer dann Verwendung, wenn einer Routine eine Speicheradresse mitgeteilt werden muß, oder das rufende Programm eine Adresse zurückgeliefert bekommt. Allgemein ausgedrückt markieren die Segment-Register immer ein Datensegment und dessen Offset im Speicher des Computers. 


nach oben

Frage: Was für Interrupte bzw. Funktionen gibt's denn nun alles?
Antwort: Im Internet gibt es mehrere, mehr oder weniger komplette, Interruptlisten. Diese sind allerdings meist so umfangreich, daß es sehr schwer fällt die nötigen Informationen herauszulesen. Ich habe daher zu den Tutorials, die sich mit Interrupten beschäftigen, oder sich solcher bedienen, eine Liste der meisten Interrupte bzw. Funktionen zusammengestellt die zu dem jeweiligen Gebiet gehören.

nach oben