diese Seite als gepacktes Word-Document runterladen (empfohlen)
Der Protected-Mode (ab 80386)
Aufbau eines Selektors im Protected-Mode:
Offset |
Größe |
Zweck |
0 |
WORD |
Segmentlänge (Bits 0-15) |
2 |
WORD |
Basisadresse (Bits 0-15) |
4 |
BYTE |
Basisadresse (Bits 16-23) |
5 |
BYTE |
Flags #1 |
6 |
BYTE |
Flags #2 |
7 |
BYTE |
Basisadresse (Bits 24-31) |
Flags #2:
Bits 0-3: Segmentlänge (Bits 16-19)
Bit 5: 0
Bit 7: Faktor für Segmentlänge ( 0 = 1 Byte / 1 = 4KB )
DOS/Extender-Interrupts im Protected-Mode
Infos über Interrupt 31h: Programmers Guide - Interrupt 31H DPMI Functions
Infos über C-Funktionen: Watcom C Library Reference Help
Interrupt 21h:
Header-Dateien ( dos.h / stdio.h / io.h / stdlib.h )
Interrupt-Vektor setzen:
Eingabe:
ah = 25h
al = Nummer des Interrupts
ds :[edx] = Adresse der neuen Interrupt-Routine
C: _dos_setvect
Interrupt-Vektor ermitteln:
Eingabe:
ah = 35h
al = Interruptnummer
Ausgabe:
es:[ebx] = Adresse der Interrupt-Routine
C: _dos_getvect
Datei erstellen:
Eingabe:
ah = 3Ch
cx = Attribut ( normalerweise 0 / siehe TASM-Buch S.426 )
ds:[edx] = Zeiger auf Dateiname
Ausgabe:
cf = Fehler
eax = Handle oder Fehlercode
C: creat / _dos_creat
Datei öffnen:
Eingabe:
ah = 3Dh
al = Zugriffscode ( 0=read only / 1=write only / 2=both )
ds:[edx] Zeiger auf Dateiname
Ausgabe:
cf = Fehler
eax = Handle oder Fehlercode
C: open / _dos_open
Datei schließen:
Eingabe;
ah = 3Eh
ebx = Handle
Ausgabe:
cf = Fehler
eax = Fehlercode ( 6 = ungültiges Handle )
C: close / _dos_close
Datei lesen:
Eingabe:
ah = 3Fh
ebx = Handle
ecx = Anzahl Bytes
ds:[edx] = Adresse des Puffers
Ausgabe:
cf = Fehler
eax = Anzahl gelesener Bytes oder Fehlercode
C: read / _dos_read
Datei beschreiben:
Eingabe:
ah = 40h
ebx = Handle
ecx = Anzahl Bytes
ds:[edx] = Adresse des Puffers
Ausgabe:
cf = Fehler
eax = Anzahl geschriebener Bytes oder Fehlercode
C: write / _dos_write
Datei löschen:
Eingabe:
ah = 41h
ds:[edx] = Zeiger auf Dateiname
Ausgabe:
cf = Fehler
eax = Fehlercode
C: remove
Dateizeiger bewegen:
Eingabe:
ah = 42h
al = Modus ( 0=absolut / 1=akt.Pos / 2=vom Ende )
ebx = Handle
ecx = Position ( Bit 16-31 )
edx = Position ( Bit 0-15 )
Ausgabe:
cf = Fehler
eax = Fehlercode
edx = neue Pos. des Dateizeigers ( Bit 16-31 )
eax = neue Pos. des Dateizeigers ( Bit 0-15 )
C: lseek
Dateilänge ermitteln:
Eingabe:
ax = 4202h
ebx = Handle
ecx,edx = 0
Ausgabe:
cf = Fehler
edx = Dateilänge ( Bit 16-31 )
eax = Dateilänge ( Bit 0-15 ) oder Fehlercode
C: filelength
DOS-Speicher anfordern:
( nur Low-Memory - nur bis 64K Größe )
Eingabe:
ah = 48h
bx (ebx ???) = Größe in Paras
Ausgabe:
cf = Fehler
eax = Segmentadresse oder Fehlercode
bx (ebx ???) = maximale Blockgröße (wenn cf=1)
C: _dos_allocmem
DOS-Speicher freigeben:
( wie DOS-Speicher anfordern )
Eingabe:
ah = 49h
es = Segmentadresse des Speicherblocks
Ausgabe:
cf = Fehler
eax = Fehlercode
C: _dos_freemem
Programm beenden:
Eingabe:
ah = 4Ch
al = Exitcode
C: exit
Interrupt 31h:
Header-Dateien ( stdlib.h )
linearen Speicher anfordern:
( wenn VMM aktiv, dann sollte in 4KB Schritten allociert werden )
Eingabe:
ax = 0501h
bx:cx = Größe des Speicherblocks
Ausgabe:
cf = Fehler
ax = Fehlercode
bx:cx = lineare Adresse
si:di = Speicher-Handle
C: malloc
linearen Speicher freigeben:
Eingabe:
ax = 0502h
si:di = Speicher-Handle
Ausgabe:
cf = Fehler
ax = Fehlercode
C: free
Watcom 32-Bit Assembler Programmierung
(näheres siehe Watcom C/C++ Users Guide - 32-bit Assembly Language Considerations)
Standartübergabe an externe Assembler-Funktionen: (andere Übergabe siehe S.6-8)
Register-Übergabe-Parameter:
Typ |
"sizeof" Typ |
Argument Größe |
Register |
char |
1 |
4 |
Exx |
short int |
2 |
4 |
Exx |
int |
4 |
4 |
Exx |
long int |
4 |
4 |
Exx |
float |
4 |
8 |
EDX:EAX || ECX:EBX |
double |
8 |
8 |
EDX:EAX || ECX:EBX |
near pointer |
4 |
4 |
Exx |
far pointer |
6 |
8 |
EDX:EAX || ECX:EBX |
Exx Reihenfolge: EAX EDX EBX ECX
Stack-Übergabe-Parameter:
Beispiel:
push EBP
mov EBP,ESP
; Hier dann Zugriff über [EBP+12] für den ersten Parameter, [EBP+16] für den zweiten.
mov ESP,EBP
pop EBP
ret
Rückgabe-Werte:
Register:
1 Byte, 2 Byte, 4 Byte, 8 Byte - AL, AX, EAX, EDX:EAX
ansonsten auf Stack:
Beispiel:
;struct int_values { int value1, value2, value3;};
RetX proc far ;C: struct int_values RetX()
mov dword ptr SS:0[ESI],71 ; value1
mov dword ptr SS:4[ESI],72 ; value2
mov dword ptr SS:8[ESI],73 ; value3
ret
Watcom Tips & Infos
(näheres siehe Watcom C/C++ Users Guide)
Dies ist die bessere Methode.
Beispiel:
extern char Funktion(char, char, int,char *);
#pragma aux Funktion = \
"start: mov cx,23"\
"jmp start"\
"mov ebx,888"\
parm [al] [ah] [edx] [es esi]\
value [al]\
modify [cx ebx];
parm:
in welche Register sollen die Parameter hinein.
value:
in dem angegebenen Register wird ein Wert zurückgegeben.
modify:
Verwendung von Variablen:
Die Variablen müssen vor der Inline-Funktion stehen.
_asm {
mov ax,778
};
#define string(parm) #parm
string( abc ) "abc"
string( "abc" ) "\"abc\""
string( "abc" "def" ) "\"abc\" \"def\""
string( \'/ ) "\\'/"
string( f(x) ) "f(x)"
parm caller [] \
value no8087 \
modify [eax ecx edx fs gs];
#pragma aux (HIGH_C) rtn1;
#pragma aux (HIGH_C) rtn2;
Mit dem Alias-Pragma kann man ein Attribut auch für andere Funktionen gelten lassen. Die Attribute werden weiter hinten besprochen. Wenn statt "*" jetzt "_*" stehen würde, hätten alle Funktionen, die das Attribut von HIGH_C besäßen, einen Unterstrich.
FALSCH:
__segment screen;
char __based(void) *scrptr;
*(screen:>scrptr) = 0x0C;
FALSCH:
void *scrptr = MK_FP(0xB800,0);
RICHTIG:
char *screen = (char *) 0xB8000; // Segment*16
screen[0] = 0x0C;
Beispiel:
; im Assembler-Modul
.DATA
.CODE
public Test_
Test_ proc far
inc eax
ret
Test_ endp
END
// im C-Modul
extern "C" int __far Test(int);