Copyright (c) 1998 by Michael Neumann, neumann@s-direktnet.de, www.s-direktnet.de/homepages/neumann; für Watcom C++ 11.0
Der Protected-Mode
(ab 80386)
·
DOS-Extender arbeiten als quasi-Betriebssystem
und sind daher langsamer als wenn man selber diese Funktionen programmiert, die
der DOS-Extender bereitstellt.
·
FAR-Zeiger nehmen jetzt 6 Bytes ein (2 Bytes für
Selektor und 4 Bytes für Offset).
·
Wenn das Paging ausgeschaltet ist, stimmt die
lineare Adresse mit der physikalischen überein.
·
Im Protected-Mode ist es möglich IO-Ports zu
sperren.
·
Der Virtual-86-Mode ist ein Kompromiß zwischen
dem Real-Mode und dem Protected-Mode. Mit dem V86-Mode können DOS-Programme
unter Protected-Mode ganz normal ablaufen.
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: Programmer’s 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++ User’s 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
·
Wenn die Parameter nicht mehr in die oben
genannten Register passen, werden sie in Schritten von 4 Byte auf den Stack
abgelegt.
·
Alle Register, außer die, in denen die Parameter waren, müssen
gesichert und restauriert werden. Die Register EAX, ECX und EDX müssen nie gesichert
werden.
·
Das Direction-Flag muß beim Rückkehr ins
Hauptprogramm gelöscht sein (CLD).
·
Assembler-Funktionen im Big-Model müssen als FAR
deklariert werden.
·
Bei der Übergabe von FAR-Pointern in z.B.
EDX:EAX (s.o.) steht in DX der Selektor und in EAX der Offset.
·
Wenn Parameter über den Stack übergeben werden,
und davor EBP auf den Stack gesichert wurde, ist die Startadresse 12 (EBP+12).
Watcom Tips & Infos
(näheres siehe Watcom C/C++ User’s Guide)
·
Inline Assembler mit „#pragma“
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:
die
hier genannten Register werden von der Inline-Funktion verändert. Die in parm genannten Register müssen hier
nicht aufgezählt werden.
Verwendung von Variablen:
Die Variablen
müssen vor der Inline-Funktion stehen.
·
Inline Assembler mit „_asm“ oder „__asm“
_asm {
mov
ax,778
};
·
32-Bit Modus
Im
32-Bit Modus geht es schneller ein DWORD zu laden als ein WORD. Das liegt
daran, daß im 32-Bit Modus nicht 66h
vor einem DWORD-Ladebefehl steht sondern vor einem WORD-Ladebefehl. Im 16-Bit
Modus ist dies genau umgedreht.
· String-Makro
#define
string(parm) #parm
string( abc ) "abc"
string( "abc" ) "\"abc\""
string( "abc" "def" ) "\"abc\"
\"def\""
string( \'/ ) "\\'/"
string( f(x) ) "f(x)"
·
Watcom C Language Reference - Programming
Style (selbst nachschauen)
·
Structured Exception Handling (siehe User’s
Guide / nur für Windows)
·
32-Bit Pragmas
·
Siehe
auch User’s Guide - 32-bit Pragmas
·
#pragma disable_message ( msg_num {, msg_num} )
[;] // nur C
·
#pragma enable_message ( msg_num {, msg_num} )
[;] // nur C
·
#pragma error "error text" [;]
·
#pragma message ( "message text" ) [;]
·
#pragma
once [;]
Wenn dieses Pragma in
einer Header-Datei steht, wird diese Datei nur einmal geladen. Das kann helfen um die Zeit des
Compilierens zu verkürzen.
·
Alias-Pragma:
#pragma aux HIGH_C "*" \
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.
·
parm-Attribute:
Hiermit wird festgelegt wie Argumente übergeben werden.
·
caller
[]: Argumente
von rechts nach links auf den Stack.
·
reverse
[]: Argumente
von links nach rechts.
·
routine: Die
aufgerufene Routine nimmt die Argumente vom Stack.
·
caller: Die aufgerufene Routine muß
nicht den Stack säubern.
·
[eax]
[es di]: Argumente
werden in diesen Register übergeben.
·
value-Attribute:
Hiermit wird festgelegt wie Werte zurückgegeben werden.
·
[ax]
...: Wert wird
in ax zurückgegeben (Describing Function Return...).
·
caller/routine...: siehe unter (Describing Function Return
Information).
·
aborts: Funktion kehrt niemehr zum
Aufrufer zurück.
·
modify-Attribute:
Hiermit wird festgelegt was die Funktion verändert.
·
nomemory: Die Funktion
verändert keine Variablen.
·
exact: Nur die Register, die
aufgelistet werden, werden verändert. Es
wird angenommen, daß die Übergabe-Register unverän- dert
bleiben, solange sie nicht aufgelistet sind.
·
[eax
ebx]..: Diese Register
werden verändert. Wenn nicht “exact“ davor steht,
wird angenommen, daß auch die Übergabe-Register (parm - value) verändert werden.
· User’s Guide - Watcom C/C++ Compiler
Options
· Users’s Guide - The Watcom C/C++ Compilers
·
Durch Pragmas
kann die normale Übergabe von Werten an externe Assembler-Funktionen geändert
werden.
·
DOS/4GW:
Speicherzugriff
Speicher kann ganz normal mit <malloc> und
<free> allociert bzw. deallociert werden. Man kann auch ganz normal
darauf zugreifen. Wenn man auf den Low-Memory-Bereich (unter 1MB) zugreifen
will muß man folgendes beachten:
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;
·
Speicher unter 1MB einfach: Pointer = Segment*16 + Offset.
·
externer Assembler:
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);
·
Hinter dem Namen der Funktion muß im Assembler-Modul
ein Unterstrich stehen.
·
Die Funktion muß immer im Assembler-Modul far sein, und im C-Modul __far.