Forum: PC-Programmierung DOS-COM mit bcc erzeugen (16bit),


von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Hi,

ich möchte mit dem bcc-compiler MSDOS-COM Programme erzeugen. Das 
funktioniert so halb...
Testen tu ich das Ganze mit dosbox auf linux.

Dieses Programm hängt die dosbox in einer Endlosschleife auf, scheint 
also geladen zu werden und zu laufen:
1
int main() {
2
  for(;;);
3
}
Disassembly:
1
00000000  55                push bp
2
00000001  89E5              mov bp,sp
3
00000003  57                push di
4
00000004  56                push si
5
00000005  EBFE              jmp short 0x5
6
00000007  5E                pop si
7
00000008  5F                pop di
8
00000009  5D                pop bp
9
0000000A  C3                ret
10
0000000B  00                db 0x00
(Ich frage mich, warum für eine Endlosschleife so viel gesichert wird - 
beim ret wird er auch nie ankommen, aber das ist wohl Compiler-Magie...)

Das jmp short dürfte nach JMP_ADDRESS + 2 + ZWEITES_BYTE = ZIEL wieder 
bei 5h landen.



Diese Programme hier tun jedoch gar nichts, sie sollen einmal per BIOS 
INT und einmal per DOS INT Zeichen ausgeben. Ich tippe test.com in der 
dosbox und ich lande sofort wieder beim Prompt, ohne das sich was tat.

DOS INT:
1
int main() {
2
  asm("mov ah, 0x02"); // ASCII 127 an Cursor schreiben
3
  asm("mov dl, 127");
4
  asm("int 0x21");
5
}
Disassembly:
1
00000000  8A260200          mov ah,[0x2]
2
00000004  8A167F00          mov dl,[0x7f]
3
00000008  CD21              int 0x21
4
0000000A  C3                ret
5
0000000B  00                db 0x00

BIOS INT:
1
int main() {
2
  asm("mov ah, 0x07");
3
  asm("mov al, 127");
4
  asm("mov bh, 0");
5
  asm("mov cx, 1");
6
  asm("int 0x10");
7
}
1
00000000  8A260700          mov ah,[0x7]
2
00000004  A07F00            mov al,[0x7f]
3
00000007  8A3E0000          mov bh,[0x0]
4
0000000B  8B0E0100          mov cx,[0x1]
5
0000000F  CD10              int 0x10
6
00000011  C3                ret
7
00000012  0000              add [bx+si],al
und hier frage ich mich, was die zwei 0-Bytes am Ende sollen.


So wie ich das verstanden habe, wird eine COM-Datei nach 0x100 geladen 
und von dort ausgeführt, aber in welchem Code-Segment? Das 
Compiler-Modell hab ich als tiny angegeben, sodass alles im selben 
Segment liegt.

Wieso werden die INTs nicht ausgeführt?

Ich hoffe, da findet sich noch jemand, der sich ein wenig auskennt.

von Icke (Gast)


Lesenswert?

Hänge doch mal deine erzeugte .COM an.
Wie bist du an dein disassembly gekommen.
Normalerweise werden COM an Adresse CS:0100 geladen.

MfG

von Thomas (Gast)


Lesenswert?

Nun bcc beziehungsweise der linker erzeugt eine exe keine com Datei.
Du mußt die exe deshalb konvertieren. Früher machte man das mit exe2bin 
oder ähnlichen Programmen.
Thomas

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Nils S. schrieb:

> (Ich frage mich, warum für eine Endlosschleife so viel gesichert wird -
> beim ret wird er auch nie ankommen, aber das ist wohl Compiler-Magie...)

Der Compiler wird einfach keine so gute Optimierung haben wie
das, was du von GCC oder Clang kennst.

> DOS INT:int main() {
>   asm("mov ah, 0x02"); // ASCII 127 an Cursor schreiben
>   asm("mov dl, 127");
>   asm("int 0x21");
> }

> Disassembly:00000000  8A260200          mov ah,[0x2]
> 00000004  8A167F00          mov dl,[0x7f]

Da stehen im Disassembly Indirektklammern.

Kann es sein, dass du das so schreiben müsstest?
1
   asm("mov ah, #0x02"); // ASCII 127 an Cursor schreiben
2
   asm("mov dl, #127");

: Bearbeitet durch Moderator
von Nils S. (kruemeltee) Benutzerseite


Angehängte Dateien:

Lesenswert?

Thomas, ich hab' vergessen, dazuzusagen, dass bcc nicht der Borland C 
Compiler ist, sondern:
https://linux.die.net/man/1/bcc
>-Md
>alters the arguments for all passes to produce MSDOS executable COM files.
>These are small model executables, use -i to get tiny model.

>Wie bist du an dein disassembly gekommen.
$ ndisasm test.com

Im Anhang eine test.com mit diesem Code:
1
int main() {
2
  asm("mov ah, 0x07");
3
  asm("mov al, 127");
4
  asm("mov bh, 0");
5
  asm("mov cx, 1");
6
  asm("int 0x10");
7
}

>Normalerweise werden COM an Adresse CS:0100 geladen.
CS kommt woher? Lasse ich Code aus dem Bootsektor ausführen oder im 
option-rom, ist das klar. Nur unter DOS... übersehe ich was? Denkfehler 
oder noch nicht gefunden?

> Kann es sein, dass du das so schreiben müsstest?
>    asm("mov ah, #0x02"); // ASCII 127 an Cursor schreiben
>    asm("mov dl, #127");
Dann hab' ich das Ergebnis vom Screenshot. Disasm:
>00000000  B407              mov ah,0x7
>00000002  B07F              mov al,0x7f
>00000004  B700              mov bh,0x0
>00000006  B90100            mov cx,0x1
>00000009  CD10              int 0x10
>0000000B  C3                ret
Nun scheint er aber was auszuführen und ich schau' mir das gleich mal 
genauer an. Sieht auf jeden Fall schonmal sehr gut aus.

Schonmal vielen Dank!

von Heinz V. (heinz_v)


Lesenswert?

Nils S. schrieb:

> ich möchte mit dem bcc-compiler MSDOS-COM Programme erzeugen. Das
> funktioniert so halb...
> Testen tu ich das Ganze mit dosbox auf linux.

> Dieses Programm hängt die dosbox in einer Endlosschleife auf, scheint
> also geladen zu werden und zu laufen:
>
1
> int main() {
2
>   for(;;);
3
> }
4
>
> Disassembly:
>
1
> 00000000  55                push bp
2
...
3
> 0000000B  00                db 0x00
4
>
> (Ich frage mich, warum für eine Endlosschleife so viel gesichert wird -
> beim ret wird er auch nie ankommen, aber das ist wohl Compiler-Magie...)

> Das jmp short dürfte nach JMP_ADDRESS + 2 + ZWEITES_BYTE = ZIEL wieder
> bei 5h landen.

Das liegt wohl an der C Calling Convention, der Stapel wird zur 
Parameterübergabe verwendet und grundsätzlich eingerichtet.

> Diese Programme hier tun jedoch gar nichts, sie sollen einmal per BIOS
> INT und einmal per DOS INT Zeichen ausgeben. Ich tippe test.com in der
> dosbox und ich lande sofort wieder beim Prompt, ohne das sich was tat.

> DOS INT:
>
1
> int main() {
2
>   asm("mov ah, 0x02"); // ASCII 127 an Cursor schreiben
3
>   asm("mov dl, 127");
4
>   asm("int 0x21");
5
> }
6
>
> Disassembly:
>
1
> 00000000  8A260200          mov ah,[0x2]
2
...
3
> 0000000B  00                db 0x00
4
>

Das sollte eigentlich funtionieren, probier es doch lieber mit 0x41 dann 
sollte ein 'a' ausgegeben werden.
> BIOS INT:
>
1
> int main() {
2
>   asm("mov ah, 0x07");
3
>   asm("mov al, 127");
4
>   asm("mov bh, 0");
5
>   asm("mov cx, 1");
6
>   asm("int 0x10");
7
> }
8
>

>
1
> 00000000  8A260700          mov ah,[0x7]
2
...
3
> 00000011  C3                ret
4
> 00000012  0000              add [bx+si],al
5
>
> und hier frage ich mich, was die zwei 0-Bytes am Ende sollen.

Von den ehemals vielen VGA Interrupts (0x10) unterstützen moderne 
Graphikkarten nur noch die die man immer braucht (Videomodus auswählen, 
CLS, usw.) Interrupts zur Semigraphik im Textmodus und andere selten 
gebräuchliche sind fortgefallen, das hat nix mit dem Betriebssystem zu 
tun sondern mit der Graphikkarte.

> So wie ich das verstanden habe, wird eine COM-Datei nach 0x100 geladen
> und von dort ausgeführt, aber in welchem Code-Segment? Das
> Compiler-Modell hab ich als tiny angegeben, sodass alles im selben
> Segment liegt.

Eine .com Datei beschränkt sich idR auf nur 64kB und Code und Daten 
liegen im selben Segment CS = DS = ES = SS
> Wieso werden die INTs nicht ausgeführt?

Lässt die Einstellung der DOS Emulation unter Linux denn die Ausführung 
der Interrupts zu?

von Thomas (Gast)


Lesenswert?

Nun es fehlt dann immer noch ein int20 oder ähnliches um das Programm zu 
beenden.
Ohne Prorammende kehrt das Programm nicht mehr zum Aufrufer 
(command.com) zurück.

Ich kenn jetzt den Compiler nicht aber com werden immer ab Offset 100 
geladen. Davor ist der PSP. Die Segmente stellt der Programm Loader ein. 
Im PSP sind Aufruf und Rückkehr Parameter sowie Pfad und Prog Parameter 
codiert.
Thomas

von S. R. (svenska)


Lesenswert?

Erstens:
Deine Adressen sehen seltsam aus.
Die erste Instruktion sollte an Adresse 0x100 stehen, nicht bei Null.

Zweitens:
COM-Dateien bestehen nur aus einem Segment (CS=DS=ES=SS) und können 
daher von jedem Segment aus ausgeführt werden. Das benutzte Segment wird 
von DOS festgelegt.

Code vom BIOS wird hingegen an die absolute Adresse 0x7C00 geladen, 
wobei dem BIOS die Wahl des Segments frei steht (0x0000:0x7C00, 
0x07C0:0x0000 oder 0x0240:0x5800 sind alle zulässig).

Drittens:
Erzeugt der bcc irgendwelchen Startup-Code für DOS? Wenn nicht, darf 
main() nicht zurückkehren, sondern dein Programm muss mit "INT 0x21, 
AH=0x4C" (DOS 2.0+) oder INT 0x20 (CP/M-Kompatiblitätsinterrupt) enden.

Viertens:
Moderne Grafik-BIOSse unterstützen die meisten VGA-Interrupts aus 
Kompatiblitätsgründen nach wie vor, denn sonst würden System-BIOS oder 
Bootloader sich selbst nicht anzeigen können.

Fünftens:
Was Linux oder das Grafik-BIOS können, spielt schlicht keine Rolle, da 
DOSBox sowohl CPU, Grafikkarte und auch das Betriebssystem emuliert (man 
kann aber DOS booten).

Sechstens:
Beachte, dass DOSBox auf gemounteten Laufwerken die Verzeichnisinhalte 
cacht. Wenn du also von Linux aus etwas änderst, solltest du vorher ein 
RESCAN ausführen.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

> Deine Adressen sehen seltsam aus.
> Die erste Instruktion sollte an Adresse 0x100 stehen, nicht bei Null.

Aber eine com-Datei hab doch keine 256bytes 0-offset, sie wird nach 100 
geladen.

> Zweitens:
> COM-Dateien bestehen nur aus einem Segment (CS=DS=ES=SS) und können
> daher von jedem Segment aus ausgeführt werden. Das benutzte Segment wird
> von DOS festgelegt.
Das entspricht dem Tiny-Modell, woher nun CS kommt, war mir nicht ganz 
klar. Danke.

> Drittens:
> Erzeugt der bcc irgendwelchen Startup-Code für DOS? Wenn nicht, darf
> main() nicht zurückkehren, sondern dein Programm muss mit "INT 0x21,
> AH=0x4C" (DOS 2.0+) oder INT 0x20 (CP/M-Kompatiblitätsinterrupt) enden.
Nein, die Disassemblys sind die kompletten Binaries. Eine habe ich 
angehangen.

Das Ziel des ganzen ist ein option ROM, welches ausschliesslich 
BIOS-INTs verwenden soll. Testen will ich das ganze aber in der dosbox, 
da ich auch von DOS aus da hin hüpfen können will. Gibt's eine 
Möglichkeit zur Laufzeit ein laufendes/nicht laufendes DOS zu erkennen?
Das müsste in der init-routine des option-roms passieren, also kurz nach 
POST. Demnach ist auch noch kein DOS geladen.
Signatur im Speicher suchen? int 0x21 ausführen und schauen ob es 
klappte oder ein Fehler auftrat?

> Sechstens:
> Beachte, dass DOSBox auf gemounteten Laufwerken die Verzeichnisinhalte
> cacht. Wenn du also von Linux aus etwas änderst, solltest du vorher ein
> RESCAN ausführen.
Zum testen geht jedes mal eine neue auf. F8 führt make aus, F5 make 
test, was die dosbox öffnet und das Programm startet.
RESCAN kannte ich aber noch nicht, das ist auch praktisch.

Thomas schrieb:
> Nun es fehlt dann immer noch ein int20 oder ähnliches um das Programm zu
> beenden.
> Ohne Prorammende kehrt das Programm nicht mehr zum Aufrufer
> (command.com) zurück.
Ein simples Programm, was nur aus NOP und RET besteht, funktioniert und 
kehrt auch zur command.com zurück.

von Thomas (Gast)


Lesenswert?

Du hast Dos nicht verstanden. Sobald du Ints verwendest ist das eben 
kein simples Programm mehr.der int20 macht auch Aufräumarbeiten. Oder 
warum glaubst du dass es nicht funktioniert?
Setze einfach mal einen int20 an das Programm Ende.
Thomas.

von S. R. (svenska)


Lesenswert?

Nils S. schrieb:
>> Deine Adressen sehen seltsam aus.
>> Die erste Instruktion sollte an Adresse 0x100 stehen, nicht bei Null.
>
> Aber eine com-Datei hab doch keine 256bytes 0-offset, sie wird nach 100
> geladen.

Nun, dann solltest du das dem Disassembler auch mitteilen.

>> Drittens:
>> Erzeugt der bcc irgendwelchen Startup-Code für DOS? Wenn nicht, darf
>> main() nicht zurückkehren, sondern dein Programm muss mit "INT 0x21,
>> AH=0x4C" (DOS 2.0+) oder INT 0x20 (CP/M-Kompatiblitätsinterrupt) enden.
> Nein, die Disassemblys sind die kompletten Binaries. Eine habe ich
> angehangen.

Faustregel: Wenn du ein DOS-Programm baust, dann sollte das auch ein 
DOS-Programm sein und wie ein DOS-Programm enden.

Der 8086 hat keinen Speicherschutz, also darfst du dein Programm auch 
gerne als Loader für dein "Option ROM" entwerfen. Der darf auch gerne 
mit "INT 0x21 / AH=4Ch" enden, wenn das Option ROM fertig ist.

Option ROMs enden übrigens mit "INT 0x18" oder "INT 0x19", je nachdem, 
ob die Bootkette weiterlaufen oder enden soll. Der DOS-Loader darf 
diesen Interrupt gerne abfangen, denn so funktionieren TSRs.

> Das Ziel des ganzen ist ein option ROM, welches ausschliesslich
> BIOS-INTs verwenden soll. Testen will ich das ganze aber in der dosbox,
> da ich auch von DOS aus da hin hüpfen können will.

Dann implementiere ein Option ROM und nimm einen Emulator, der Option 
ROMs ausführen kann (z.B. Qemu). Oder implementiere ein DOS-Programm, 
welches das Option ROM in einer Umgebung ausführen kann, die der realen 
Umgebung ungefähr entspricht. DOS ist erstmal keine solche.

> Gibt's eine
> Möglichkeit zur Laufzeit ein laufendes/nicht laufendes DOS zu erkennen?

Wozu? Wenn du das Option ROM von DOS aus aufrufst, dann weißt du, dass 
DOS läuft und kannst deine eigene Signatur hinterlegen. Einfach eine 
DOS-Funktion aufrufen wird crashen, wenn DOS nicht da ist.

> Ein simples Programm, was nur aus NOP und RET besteht, funktioniert und
> kehrt auch zur command.com zurück.

Ja, weil die Entwickler vom DOSBox-DOS nett waren und dir einen Sprung 
nach CS:0000 auf den Stack gelegt haben (am Anfang des PSP steht ein 
"INT 0x20").

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Das komplette Programm

S. R. schrieb:
> Dann implementiere ein Option ROM und nimm einen Emulator, der Option
> ROMs ausführen kann (z.B. Qemu). Oder implementiere ein DOS-Programm,
> welches das Option ROM in einer Umgebung ausführen kann, die der realen
> Umgebung ungefähr entspricht. DOS ist erstmal keine solche.

Das mache ich auch mit qemu. Jedoch brauche ich auch einen Loader dafür, 
der von DOS aus rennt. Das meiste vom Code sollte in das ROM wandern. So 
stell ich mir das ungefähr vor:
1
rom_init_while_post:
2
  ...
3
  ; tastendruck? => jmp anwendung_entry
4
  ; boot prozess gleich fortsetzen
5
  int 0x18/0x19
6
  ret
7
  
8
  
9
anwendung_entry:
10
  ; läuft dos? flag setzen
11
  ; kein dos? dann anwendung in ram laden
12
  ...
13
  ; kein dos? free ram, zustand für bootprozess wiederherstellen
14
  ; ende mit 0x21 wenn dos
15
  ret

Thomas schrieb:
> Du hast Dos nicht verstanden. Sobald du Ints verwendest ist das eben
> kein simples Programm mehr.der int20 macht auch Aufräumarbeiten. Oder
> warum glaubst du dass es nicht funktioniert?
> Setze einfach mal einen int20 an das Programm Ende.

Ein int 0x20 und int 0x24/AH=4C ändert nichts am Fehlverhalten wie auf 
dem screenshot zu sehen.
Der int 10 wird ja nach dem Beenden ausgeführt, ist vom BIOS und 
zumindest bis zum Zeichen ausgeben müsste alles gehen. Der Fehler liegt 
also noch woanders.
z.B. Fehler wie im Screenshot (disasm origin mitgeteilt diesmal):
>00000100  B407              mov ah,0x7
>00000102  B07F              mov al,0x7f
>00000104  B700              mov bh,0x0
>00000106  B90100            mov cx,0x1
>00000109  CD10              int 0x10
>0000010B  B44C              mov ah,0x4c
>0000010D  CD21              int 0x21
>0000010F  C3                ret

S. R. schrieb:
> Einfach eine
> DOS-Funktion aufrufen wird crashen, wenn DOS nicht da ist.

An den Crash dachte ich und daran, den Fehler abzufangen. z.B. wie auf 
nicht vorhandene OpCodes zu testen (z.B. wie bei 286/386 Unterscheidung 
anhand des Befehlssatzes).
Aber selbst die Signatur zu hinterlegen oder einen zweiten 
Einsprungpunkt zu bauen, scheint einfacher zu sein.

S. R. schrieb:
> Ja, weil die Entwickler vom DOSBox-DOS nett waren und dir einen Sprung
> nach CS:0000 auf den Stack gelegt haben (am Anfang des PSP steht ein
> "INT 0x20").
Was würde denn sonst passieren? (auch @Thomas)
Das ret läuft ins Nichts, weil er die falsche Rücksprungadresse bekam?


Ich denke, ich werde mir da jetzt erstmal was mit Borland C oder tasm 
bauen, dann versuche ich das auf einer anderen Plattform hinzubekommen. 
Wurmt aber schon ein bisschen...

von S. R. (svenska)


Lesenswert?

Nils S. schrieb:
> Jedoch brauche ich auch einen Loader dafür, der von DOS aus rennt.
> Das meiste vom Code sollte in das ROM wandern. So
> stell ich mir das ungefähr vor:

Warum soll das Option-ROM irgendwelche Erkennungen machen? Baue doch 
lieber den Aufruf des BIOS nach:
- Loader:    0x100 bis 0x400 (also eine normale COM-Datei)
- OptionROM: 0x400 bis (whatever), aber mit ORG 0x0000 verziert.

Ablauf:
- (optional: Magic Number und Checksumme prüfen und evtl. abbrechen)
- Hook auf INT 0x18/0x19 platzieren
- FAR CALL nach (CS+0x40):0x0003
- bei Rückkehr Fehlermeldung ausgeben

Wenn ein INT 0x18/0x19 ankommt:
- Hook wieder entfernen
- INT 0x21/0x4C

Fertig. Damit bildet der Loader einfach das BIOS nach und muss über die 
Struktur des ROMs kein weiteres Wissen haben.

Das Option ROM kann anhand seiner eigenen Adresse unterscheiden, ob es 
vom Loader geladen wurde (CS < 0xC800) oder nicht.

>> Einfach eine DOS-Funktion aufrufen wird crashen, wenn DOS nicht da ist.
> An den Crash dachte ich und daran, den Fehler abzufangen.

Schlechte Idee. Ein 8086/8088 hat kein "Invalid Opcode".

> S. R. schrieb:
>> Ja, weil die Entwickler vom DOSBox-DOS nett waren und dir einen Sprung
>> nach CS:0000 auf den Stack gelegt haben (am Anfang des PSP steht ein
>> "INT 0x20").
> Was würde denn sonst passieren? (auch @Thomas)
> Das ret läuft ins Nichts, weil er die falsche Rücksprungadresse bekam?

Ja. Dass der Stack immer auf einen INT 0x20 zeigt, ist ein 
Kompatiblitätsartefakt von CP/M und funktioniert nur, wenn CS seit dem 
Programmstart nicht verändert wurde. Unter DOS 2.0 und höher empfiehlt 
Microsoft den INT 0x21/0x4C.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Ich baue sowas gerade. BIOS Extension wird via int 19 geladen.In meinem 
Fall ab 0xe000:0
Das BIOS checked die Quersumme der ersten 4 Paras und startet dann mit 
einem Far Ret
Mein Programm ab E002:0 Dort steht mein modifizierter startupcode mit 
dem Label arcuse.
Ab diesem Zeitpunkt kann dann in C programmiert werden. Ich verwende 
allerdings die Borland Tools. Der einzige Unterschied ist dass ich 
meinen Code nach dem linken durch einen selbstgeschriebenen locator 
schicke. Dieser macht aus der Exe ein binary indem er den exeheader 
auflöst. Als Debugger verwende bochs bzw TD wenn ich int 3 exceptions 
bekomme.
Mein Ziel ist es auf einem NEC V40 nativ dem MFA Monitor / Assembler zu 
starten.
MFA ist ein altes 8085 Lernsystem. Die NEC V Serie kann nativ 8085 Code 
ausführen.

Thomas

von S. R. (svenska)


Lesenswert?

Laut Dokumentation sollte, wenn dein ROM ab E000:0 steht, der 
Einsprungpunkt E000:2 sein. Außerdem sollte das BIOS, wenn die Größe im 
Header korrekt angegeben ist, die Checksumme über das gesamte ROM prüfen 
und nicht nur über die ersten Parameter.

Laut Dokumentation kann der V40 nur 8080-Code nativ ausführen, nicht 
8085-Code (die scheinen aber nahezu identisch zu sein). Ansonsten ist 
das irgendwie auch eine coole Idee, einen Hypervisor für CP/M zu bauen. 
:-)

von Thomas (Gast)


Lesenswert?

Nein der Einsprung ist seg:0003 . 4 Paras heißt es wird über 2048 Bytes 
die Checksumme geprüft.
1
entry: db 55h,aah      ;Magic
2
           db 4.                 ;4*512 Bytes
3
           jmp short Setup
4
           db 0.                 ;Space for chksum
5
6
;setup our own int19 vector
7
Setup proc far
8
            push DS
9
            push 0
10
            pop  DS          ;seg 0
11
            mov word ptr DS:[19h*4+0],Offset INT19Boot
12
            mov word ptr DS:[19h*4+2],CS
13
            pop DS
14
            ret
15
Setup endp
16
17
INT19Boot proc far
18
            mov ax, CS.           ;cs +2 will be our entry
19
            inc     ax
20
            inc     ax
21
            push  ax
22
            push  0
23
            retf                         ; Start will be e002:0
24
INT19Boot endp
Thomas

von S. R. (svenska)


Lesenswert?

Ah, dann habe ich einfach nur missverstanden. Danke für den Hinweis.

von Nils S. (kruemeltee) Benutzerseite


Lesenswert?

Mittlerweile hab ich das hinbekommen, was ich wollte.

Hier ein Beispiel, welches funktioniert.
Funktioniert mit oder ohne return_to_dos() auf DOS.
1
void putc(c);
2
void print_str(char *s);
3
void return_to_dos();
4
5
unsigned short ret_test();
6
7
#define uint8_t  unsigned char
8
#define uint16_t unsigned short
9
10
int main() {
11
  uint8_t a;
12
  uint16_t retval;
13
  
14
  a = 1+5;
15
  a += '0';  // convert to ASCII, more than 9 won't work this way
16
  putc(a);
17
  
18
  print_str("\r\n");  // new line on DOS
19
  
20
  retval = ret_test();
21
  
22
  if(retval == 0x1234) print_str("return-test ok!");
23
  
24
  return_to_dos();
25
  
26
}
27
28
/* ret_test()
29
 * 
30
 * this example shows how to return data from inline assembler to C.
31
 * 
32
 * simple ints are returned via AX register. On x86 DOS an int equals
33
 * unsigned short. 
34
 * 
35
 */
36
unsigned short ret_test() {
37
  #asm
38
    mov ax, #0x1234  ; "return 0x1234"      
39
  #endasm
40
}
41
42
void print_str(char *s) {
43
  while(*s) {
44
    putc(*s);
45
    s++;
46
  }
47
}
48
49
void putc(c) {
50
  #asm
51
    mov ah, #0x0E  ; int function Eh, write char, move cursor
52
    mov bx, sp
53
    mov al, [bx+2]  ; arguments are on stack
54
    mov bh, #0  ; page
55
    mov cx, #1  ; number of chars
56
    int 0x10
57
  #endasm
58
}
59
60
void return_to_dos() {
61
  #asm
62
    mov ah, 0x4c
63
    int 0x21
64
  #endasm
65
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.