|
|
||
|
|
||
| Inlineassembler | ||
| .. Grundlagen | ||
|
|
||
|
Inlineassembler
(Assemblercode im C
-Programm)
Es ist möglich, den Quelltext eines
C-Programms mit Sprachbrocken der Assemblersprache zu garnieren.
Normaler Weise übersetzt bei C ein Compiler alle
C-Befehlsworte in die Maschinensprache. Die
Assembler-Programmanteile dagegen, müssen durch ein
Assemblerprogramm übersetzt werden. Ein solches muß
natürlich in der IDE vorhanden sein. Der Linker bindet beide
Programmteile zusammen und die CPU bekommt die
Ursprungs-Programmiersprache nicht mehr mit, denn beide Teile
bestehen aus Maschinensprache.
asm {} Der C -Schlüsselbefehl zum Übergang von der C- zur Assembler-Programmierung ist der Befehl asm{}. Er beendet das Compilerprogramm und startet das Assemblerprogramm, so eines vorhanden ist. Nach seiner zweiten Blockklammer } wird das Assemblerprogramm beendet und wieder das Compilerprogramm gestartet. Seine Mnemonik lautet .. asm {..} Zwischen seinen geschweiften Klammern stehen die Assemblerbefehle. Wird nur ein einziger Assemblerbefehl genannt können die geschweiften Klammern entfallen. In den Assemblerbefehlen können C-Variable benutzt werden, wenn Sie von einem Typ sind, der auch in die Register der CPU hineinpasst. Doch zunächst ein Blick auf die Namen und Speichergrössen der CPU-Register ... Die Register der 80x86 CPU Eine umfassende Beschreibung dieser Register befindet sich im Kapitel über die Assembler-Programmierung der Intel 80x86 CPU im Real Mode unter .. [Register der 80x86 CPU] Für einen schnellen Überblick reicht es, die Namen der Allzweck-Benutzer -Register zu kennen. Ihre Namen lauten A, B, C, und D. Ein zusätzlicher Buchstabe zu den Namen gibt an, mit wie vielen Bits die Register benutzt werden sollen. Allzweckregister der ALU ----------------------------- (16 Bit) AX, BX, CX, DX ( 8 Bit) AH-AL, BH-BL, CH-CL, DH-DL X gibt an, dass das entsprechende Register mit 16 Bit in einem Assemblerbefehl benutzt werden soll, während H und L für high und low stehen und das genannte Register in zwei unabhängige 8Bit-Portionen teilt. Da beide Portionen das gleiche X-Register betreffen ergibt sich zudem folgende Kuriosität. Besitzt das AH-Register den Wert AFh und das AL-Register den Wert FEh, so ist das AX-Register mit dem Wert AFFEh gefüllt. Je nach Assemblerbefehl kann man folgend nur das AH oder das AL oder das AX -Register benutzen. C-Variablentypen für die Register -------------------------------------- (16 Bit) unsigned oder int ( 8 Bit) signed char oder unsigned char Die C-Variablentypen müssen zwingend der Größe der CPU-Register entsprechen. Sie dürfen weder größer noch kleiner sein. So kann ein X-Register mit 16 Bit nur unsigned oder int Variable aufnehmen. Nicht jedoch char oder long. |
||
|
Das nachfolgende Programm zeigt, wie
Inlineassembler und C- Programmteile miteinander kombiniert
werden können. Dabei sind die Inlineassembler -Teile in
einer C-Funktion untergebracht, welche nach den bekannten
Methoden zwei Parameter übergeben bekommt und einen
Parameter zurückgibt.
Beispiel einer Funktion in Inlineassembler: malnehmen() |
||
|
|
|
|
#include <stdio.h>
#include <conio.h>
// malnehmen()
// Inlineassembler -Funktion, die zwei vorzeichenbehaftete Bytes
// (C-Typ: signed char, 1Byte) multipliziert und in einem Word
// (C-Typ: int, 2Byte)zurückgibt
int malnehmen(signed char fakt1, signed char fakt2)
{
asm{ mov AL, fakt1 // Register AL mit dem Faktor fakt1 laden
imul fakt2 } // und mit Faktor fakt2 multiplizieren
return(_AX); // Ergebnis in Register AX zurückgeben
}
// Beispielaufruf von malnehmen
void main(void)
{
signed char x=-3, y=6;
int erg = 0;
erg = malnehmen(x,y);
printf("Das Ergebnis von %d * %d = %d\r\n", x, y, erg);
getch();
}
|
|
|
|
|
|
|
|
|
Bildschirmausgabe |
|
Das Ergebnis von -3 * 6 = -18 |
||
| Besonderheit des Programms Mit dem Schlüsselwort return() wird wieder die Sprache C verwendet. Der Ausdruck _AX stellt eine Möglichkeit dar, unmittelbar aus der Sprache C heraus, auf die Register der CPU zuzugreifen. Diese werden in C grundsätzlich mit großen Buchstaben geschrieben, vor denen ein Unterstrich steht. Alternativ hätte man also das Ergebnis in AX auch erst per Assemblersprache einer C-Variablen zuweisen und diese dann zurückgeben können. Das Programm hätte dann folgend ausgesehen .. |
int malnehmen(signed char fakt1, signed char fakt2)
{
int erg = 0;
asm{ mov AL, fakt1 // Register AL mit dem Faktor fakt1 laden
imul fakt2 // und mit Faktor fakt2 multiplizieren
mov erg, AX } // Ergebnis in Register AX nach C-Variable
return(erg); // Ergebnis in der C-Variablen zurückgeben
}
|
||
|
|
|
|
|
Assemblerbefehle
Die
Inlineassembler -Funktion basiert auf den Assemblerbefehlen
mov
und imul.
Deren Beschreibung ist im Kapitel über
die Assembler 8086 Programmierung unter .. [Befehlssatz
der CPU 80x86]
zu finden. mov bedeutet bewege nach und imul ist ein
Multiplikationsbefehl für Ganze Zahlen. Die übergebenen
C-Variablen werden also durch Assemblerbefehle multipliziert und
das Ergebnis in einer C-Variablen zurückgegeben.
Der mov-Assemblerbefehl baut sich dabei wie
alle Assemblerbefehle mit zwei Parametern wie folgt auf ..
Befehl
Ziel, Quelle
-------------------------
mov AL,
fakt1
mov erg, AX
.. also, bewege (kopiere) nach Register AL den Inhalt der C-Speicherzelle fakt1 bzw. bewege nach der C-Speicherzelle erg, den Inhalt des CPU-Registers AX. Es ist zu beachten, dass am Ende der Assemblerzeilen kein Semikolon steht und bei Assemblerbefehlen die Groß- / Kleinschreibweise keine Rolle spielt. Es sei denn es werden C-Variable genannt. |
||
|
|
||
|
Softwareinterrupts
und der Assemblerbefehl int
x
Softwareinterrupts (kurz SoftINT) sind
indirekte Sprünge zu Unterprogrammen, die sich im BIOS, dem
Betriebssystem oder in Anwenderprogrammen (häufig als
Treiberprogramme bezeichnet) befinden. Sie werden durch den
Assemblerbefehl int x ausgelöst wobei das x für eine
Zahl zwischen 0 und 255 (0h-FFh) steht. Indirekt bedeutet, dass
die Orte (Adressen) an denen die Unterprogramme beginnen, nicht
unmittelbar von der CPU aufgerufen werden, sondern dass diese
zuvor in einem Satz von vier RAM-Speicherzellen nachsehen muss,
wo sich das Unterprogramm überhaupt befindet.
1.
es gibt 256 Softwareinterrupts die durch den Befehl int x
(x=0-255) erreicht werden können
Die Anfangsadresse des aufzurufenden Unterprogramms ist also der Inhalt von vier Bytes, die sich im Arbeitsspeicher des Computers befinden. Bei der Möglichkeit 256 solcher Sprünge angeben zu können, müssen im Arbeitsspeicher also 4*256 RAM-Speicherbytes reserviert sein, damit die Adressen für 256 Unterprogramm hineinpassen. Die hierfür reservierten Bytes sind an dessen Anfang untergebracht und besitzen die Adressen 0-1023 (0-3FFh). Ihre Inhalte die ja wiederum Adressen sind, die auf Unterprogramme zeigen, bezeichnet man entsprechend als Zeiger oder Vektoren. 2. Die 256 Softwareinterrupt-Vektoren sind in den RAM- Speichern 0-3FFh des Arbeitsspeichers einer Intel-CPU untergebracht. Die Softwareinterrupts stellen eine elegante Möglichkeit der Intel-CPUs dar um interessante Unterprogramme des BIOS oder des Betriebssystems bei Multitask-Systemen aufrufen zu können, ohne zu wissen, wo sich diese dort befinden. Natürlich besteht eine übergreifende Absprache zwischen den Systemprogrammierern, welcher Softwareinterrupt benutzt werden muss um an diese unbekannten Programmorte zu gelangen. Die Softwareinterrupts sind in den Dokumentationen zu der Betriebssoftware beschrieben. Die letzte Frage, wie denn die Adressvektoren, nach dem Start eines Computers, in die leeren RAM-Speicher 0-3FFh gelangen, ist einfach beantwortet. Sie werden vom BIOS oder dem Betriebssystem in seiner Initialisierungsphase (also beim Hochlaufen des Systems) dort hineinkopiert. Mehr zu den SoftINTs im Kapitel: [Interrupts der CPU 80x86] |
||
|
Das nachfolgende Programm demonstriert den
Aufruf eines Unterprogramms im Betriebssystem mit Hilfe eines
SoftINT.
Betriebssystem können die aktuelle
Uhrzeit ermitteln. Diese basiert auf der Zeit einer
batteriegebufferten Echtzeituhr (RTC -RealTimeClock) die auf dem
Mainboard aufgebaut ist. Beim Start des Betriebssystems wird die
RTC abgefragt und dann fortgesetzt. Die Abfrage der Zeit im
Betriebssystem kann durch den Softwarinterrupt 21h geschehen,
wenn man zuvor das AH-Register der CPU mit dem Wert 2Ch lädt.
Beispiel, Aufruf des SoftINT 21h, Funktion AH=2Ch |
||
|
|
||
#include <stdio.h> // für printf()
#include <conio.h> // für getch()
// globale Variablen, welche die Rückgaben aufnehmen sollen
unsigned char stunde, minute, sekunde;
// holezeit()
// Funktion zum Aufruf des Softwareinterrupts INT 21h und seines
// Unter-Unterprogramms AH=2Ch, 'hole die Zeit des Betriebssystems'
// Die Zeit wird von den CPU-Registern CH,CL,DH in die globalen
// C-Variablen übertragen.
void holezeit(void)
{
asm{ mov AH, 2Ch // Wahl des Unter-Unterprogramms
int 21h // im SoftINT 21h und Aufruf
// Rückgaben ..
mov stunde, CH // Stunde 0-23 in CH
mov minute, CL // Minute 0-59 in CL
mov sekunde, DH } // Sekunde 0-59 in DH
}
// Testprogramm main()..
// ruft die Funktion holezeit() auf und gibt die globalen C-
// Variablen 'Stunde', 'Minute' und 'Sekunde' im Format
// std:min:sec zum Bildschirm aus.
void main (void)
{
holezeit();
printf("\n%0.2d:%0.2d:%0.2d",stunde,minute,sekunde);
getch();
}
|
||
|
|
||
|
|
Bildschirmausgabe (Beispiel) |
|
17:33:14 |
||
|
Besonderheit des Programms
Es gibt 256 Softwareinterrupts, aber weit
mehr interessante Unterprogramme im BIOS und dem Betriebssystem.
Die Systemprogrammierer haben sich darauf geeinigt mehr
Unterprogramme zugänglich zu machen, indem sie
Softwareinterrupt-Programm als Verteilerprogramme programmieren.
Bei diesen wird durch einen Wert im AH-Register der CPU zu
Unter-Unterprogrammen verzweigt. Auf diese Weise wächst die
Anzahl der theoretisch erreichbaren Unterprogramme auf 256*256 =
65535 an.
Weitere Beispiele unter [Software Interrupts / C und Inlineassembler] |
||
|
|
||
Regeln im Überblick
|
||
|
|
.de