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: 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

 

Watcom Tips & Infos

(näheres siehe Watcom C/C++ User’s 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:

  • 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.

     

    _asm {

    mov ax,778

    };

     

  • 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.
  • #define string(parm) #parm

    string( abc ) "abc"

    string( "abc" ) "\"abc\""

    string( "abc" "def" ) "\"abc\" \"def\""

    string( \'/ ) "\\'/"

    string( f(x) ) "f(x)"

     

  • 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.
  • #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.

  •  

     

     

     

     

     

  • 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;

     

    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);