'Hello World' mit BCC 3.x und tlink
-
um dem leid ein ende zu setzen:
ich habe mir den älteren tcc 2.01 (museum, freeware) gezogen.durch die banale zeile
E:\TC>tcc -mt -lt hello.ckann man ein comfile generieren.
achtung: ich denke, du hast dir deine c0t zerstört. mach sie mal auf mit einem editor. sollte dort die errormessage mit dem printf per text zu sehen sein, ist sie überschrieben und du solltest sie neu installieren.
hier der passende compiler
http://bdn.borland.com/article/0,1410,20841,00.html
obwohl es mit dem bcc dann nach neuinstallation der c0t auch geht.so long und thats it.
übrigens: oben bei dir fehlt die option L für die angabe des library verzeichnisses.
-
vielleicht musst du "using namespace std;" erst schreiben bevor int main.
-
Daß´man mit “BCC -mt -lt hello.c“ ein com-File erzeugen kann, hatte ich ja schon vor 2 Wochen herausgefunden.
Leider mit dem Hinweis “Fatal: Cannot generate COM file : segment-relocatable items present“
Weshalb ich zur Analyse von hello.asm über tlink versuchte zu gehen (s. Forumtips auch bezüglich “interrupt“ und “setvect“).Da es ja mit dem bcc-integrierten Linker auch geht, vermute ich hier eher nicht, daß das c0t (zumal das Datum und innere Aussehen zu stimmen scheint) beschädigt ist.
Daß es keine DOS-Spezialisten mehr für C/C++ gibt, habe ich jetzt auch gemerkt (mir ist DOS hier leider durch bestehende Programme zwingend vorgeschrieben), muß aber irgendwie da durch.
Vielleicht kann man wohl wenn überhaupt nur noch Michael Tischer persönlich fragen.Das jetzt nachgetragene L für das Library-Path hat leider auch nichts gebracht, da ich c0t / c0s eh schon im Arbeitsverzeichnis hatte (sicherheitshalber für die vielen Tests, die ich schon gemacht habe auch cs.lib, emu.lib maths.lib und so manches Andere...)
Meine Ungeduld möge man mir verzeihen, da ich eben ziemlich verzweifelt bin und ich einfach dachte, daß irgend jemand für “hello world“ doch ‘mal eben‘ eine tlink-Kommandozeile angeben könnte, aber das mit so einem vorgeschriebenen 'DOS-Fossil-Programm' ist eben aus der Zeit (hätte in meiner Jugend eben mal öfter programmieren sollen, doch nun ist’s zu spät und es gibt kaum noch DOS-Know-How).
Bleibe jetzt auch bei meinem letzten Thread hier (keine Angst; mache keinen Neuen auf) und schreibe selber vielleicht irgendwann noch die Antwort, wenn ich je eine funktionierende tlink-Kommandozeile für hello.obj gefunden haben sollte. Teste weiter...
-
und wie wärs, einfach den obigen tcc zu nutzen?
der funktioniert prima wie du siehst. die *.coms bevölkern meine platte.na ja. ich klinke mich dann aus, vielleicht kann ja jemand mit 'nem bcc analog den richtigen pfad für tlink basteln.
-
Um bcc oder tcc geht es hier weniger (die Probleme mit "interrupt" und setvect gibt es sowohl mit bcc als auch tcc), sondern tlink zum Laufen zu bewegen.
BCC kann man sich übrigens glaube ich auch unter dem Suchbegriff "Wolfenstein" als "Free BCC" aus dem Netz downloaden.
Es kann sich ja wirklich nur um falsch oder fehlend zugelinkte Libraries handeln.
Experimentiere noch...
-
Du hast auch das -mt beim Compileraufruf vergessen. BTW, wenn du das Linken auf die Art und Weise nicht hinkriegst, ist das nicht direkt ein Problem. Der Weg über den Assembler sollte ja nur dazu sein, ein Listing-File zu erzeugen (tasm /la glaub ich) und dort drin nachzusehen, welche Anweisungen relozierbare Adressen beinhalten.
-
das -mt habe ich natürlich nicht "vergessen", sondern eben bewußt weggelassen, weil ich ja nicht mit dem in bcc integrierten Linker arbeiten will (das war ja der Sinn des ganzen Threads).
Inzwischen kann ich aber wenigstens das hello.com-file mit tlink erzeugen:
tlink /t /v c0t.obj+hello.obj,hello.com,,graphics.lib emu.lib maths.lib cs.libAber mein als EXE lauffähiges RS485-Programm will nicht zum .com-file werden:
tlink -t -ms -ls c0s+rs485.obj, rs485.com,, cs /lc:\bc\lib
Fatal: Cannot generate COM file : stack segment presentJetzt suche ich verzwifelt das Stack-Segment im Assemblerlisting des Compilers.
Ob es etwas mit globalen Variablen zu tun hat?:
_BSS segment word public 'BSS'
-
ähm.. -mt ist das compilerflag, kein linker.. ladida.
ohne compilierst du kein tiny.
-
elise schrieb:
ähm.. -mt ist das compilerflag, kein linker.. ladida.
ohne compilierst du kein tiny.Daß -mt für bcc ist, weiß ich schon. Aber egal, ob ich mit oder ohne -mt compiliere bleibt der Linkerfehler
Fatal: Cannot generate COM file : stack segment presentHilfreich wäre auch die Erklärung zu DGROUP, was mir gar nichts sagt.
Zu beginn der Interruptroutine erhalte ich nämlich
push bp
mov bp,cs:DGROUP@
mov ds,bp
mov bp,sp
sub sp,2
was ich (um das nicht linkbare "interrupt" zu umgehen) so im Sourcefile gar nicht umsetzen kann, weil
asm mov bp,cs:DGROUP@
als "undefined Symbol DGROUP" zurückgewiesen wird.Der Unterschied zu -mt und ohne -mt ist übrigens zum einen
mov bp,cs:DGROUP@
ohne -mt bzw. mov bp,DGROUP mit -mt und weiter
extrn DGROUP@:word
ohne -mt und mit -mt fehlt diese Externverweiszeile ganz.
-
user4711 schrieb:
Daß -mt für bcc ist, weiß ich schon. Aber egal, ob ich mit oder ohne -mt compiliere bleibt der Linkerfehler
Fatal: Cannot generate COM file : stack segment presentUnd das nimmst du als Grund, -mt nicht mehr zu verwenden? Versuch mal systematischer vorzugehen.
-mt schaltet den Compiler in das Speichermodell Tiny, das bedeutet, dass alle Segmentregister mit demselben Wert initialisiert werden, und somit Code, Daten und Stack sich ein Segment teilen. Das ist die Voraussetzung für ein COM-File, ohne Tiny kein COM.
Du musst also herausfinden, warum du diese Fehlermeldung trotz Tiny bekommst, und nicht einfach wieder auf Small (Standardeinstellung) stellen.
Hilfreich wäre auch die Erklärung zu DGROUP, was mir gar nichts sagt.
Zu beginn der Interruptroutine erhalte ich nämlich
push bp
mov bp,cs:DGROUP@
mov ds,bp
mov bp,sp
sub sp,2
was ich (um das nicht linkbare "interrupt" zu umgehen) so im Sourcefile gar nicht umsetzen kann, weil
asm mov bp,cs:DGROUP@
als "undefined Symbol DGROUP" zurückgewiesen wird.Du gehst pauschal davon aus, dass interrupt nicht linkbar ist. Darf ich erfahren, wieso? Hast du das überprüft? Bist du 100% sicher?
-
Stimmt schon; -mt lasse ich in Zukunft drin. Hatte langfristig auch nie vor, tiny abzuschalten, zumal ganz am Ende von allem ja mal ein Device-Treiber (com=sys=tiny) stehen soll.
“interrupt“ selber ist schon linkbar, aber setvect(), das diesen Linkerfehler "segment-relocatable item" verursacht, offenbar nicht, denn wenn ich setvect() auskommentiere, bekomme ich das rs384-Program ja auch als com gelinkt (nur läuft es ohne die Interruptfunktion natürlich nicht mehr wunschgemäß).
Im Listing wird das "segment-relocatable item" setvect() wie folgt aufgerufen:
mov ax,seg newserialint
push ax
mov ax,offset newserialint
push ax
mov ax,12
push ax
call near ptr _setvect
add sp,6
Hatte ja auch schon einmal vor setvect durch die folgenden Zeilen zu ersetzen, aber beim ersten ausgelösten Interrupt stürzt das Programm damit ab:
asm push es
asm push bx
asm xor bx,bx
asm mov es,bx
asm mov bx,12
asm shl bx,1
asm shl bx,1
asm pushf
asm cli
asm mov ax,word ptr [newserialint]
asm mov es:[bx],ax
asm mov ax,word ptr [newserialint+2]
asm mov es:[bx+2],ax
asm popf
asm pop bx
asm pop es
asm sti
(kann newserialint dazu aber nur als void* deklarieren, da
asm mov ax,word ptr [newserialint] natürlich nicht genommen wird, wenn newserialint nicht als void* sondern interrupt deklariert ist.)
-
user4711 schrieb:
“interrupt“ selber ist schon linkbar, aber setvect(), das diesen Linkerfehler "segment-relocatable item" verursacht, offenbar nicht, denn wenn ich setvect() auskommentiere, bekomme ich das rs384-Program ja auch als com gelinkt (nur läuft es ohne die Interruptfunktion natürlich nicht mehr wunschgemäß).
Im Listing wird das "segment-relocatable item" setvect() wie folgt aufgerufen:
mov ax,seg newserialint
push ax
mov ax,offset newserialint
push ax
mov ax,12
push ax
call near ptr _setvect
add sp,6Ich hab das mal überprüft. Der Grund für dieses Problem ist nicht ganz einsichtig, der Compiler könnte hier auch einfach das aktuelle Codesegment statt seg newserialint übergeben, da die Prozedur newserialint im selben Segment liegt. Er geht da von Annahmen aus, die im Tiny-Modell bzw. als COM-Datei nicht zutreffen, das ist eine kleine Compilerschwäche. Mit ein bisschen Casting-Magie kann man dem aber abhelfen:
// statt setvect(12, newserialint); void near (*tempint)() = (void near (*)())newserialint; setvect(12, (void interrupt (*)())tempint);
Ich schneide dem Funktionszeiger praktisch den Segmentanteil weg und ersetze ihn durch das aktuelle Codesegment. Statt
mov ax, seg newserialint push ax
steht da jetzt nur noch das (an dieser Stelle!) gleichwertige
push cs
und das hätte der Compiler von vornherein machen können.
Hatte ja auch schon einmal vor setvect durch die folgenden Zeilen zu ersetzen, aber beim ersten ausgelösten Interrupt stürzt das Programm damit ab:
asm push es
asm push bx
asm xor bx,bx
asm mov es,bx
asm mov bx,12
asm shl bx,1
asm shl bx,1
asm pushf
asm cli
asm mov ax,word ptr [newserialint]
asm mov es:[bx],ax
asm mov ax,word ptr [newserialint+2]
asm mov es:[bx+2],ax
asm popf
asm pop bx
asm pop es
asm sti
(kann newserialint dazu aber nur als void* deklarieren, da
asm mov ax,word ptr [newserialint] natürlich nicht genommen wird, wenn newserialint nicht als void* sondern interrupt deklariert ist.)Das ist eine mögliche Implementation von setvect, anscheinend auch korrekt. Bis auf die Tatsache, dass newserialint als far-Zeigervariable erwartet wird. Wenn du den setvect-Aufruf einfach durch obigen Code ersetzt hast, dann ist newserialint aber eine Funktion, du hast also praktisch die ersten vier Bytes der Funktion in die Interruptvektortabelle geschrieben
mov ax, word ptr[newserialint] würde ich mal durch mov ax,cs ersetzen.
mov ax, word ptr[newserialint+2] durch sowas wie mov ax, offset newserialint, aber da bin ich nicht sicher.Du könntest diesen Code in eine void mysetvect12(void interrupt (*newserialint)()) auslagern, dann würde es z.B. funktionieren -- bis auf die Tatsache, dass du dir dadurch wieder ein relocatable item einhandelst ;). Ach ja, die Funktion muss natürlich als interrupt deklariert bleiben.
-
Also erst einmal vielen Dank und großen Respekt vor dem für mich als Halblaien völlig unverständlichen aber compilierbaren
"void near (*tempint)() = (void near (*)())newserialint; "
"setvect(12, (void interrupt (*)())tempint); "
Das klappt als .com-Datei wirklich! Klasse !Zurück zu meinem Devicetreiber:
(weiß eh nicht, ob es nicht doch ein Fehler war, den mit C++ zu schreiben, statt Assembler zu lernen, denn da werde ich wohl noch mehr komplexe Konstrukte brauchen.)c0t.obj und cs.lib lassen sich wohl auch nicht dazu linken. Zuminest stürzt bei der Ausführung dann mit c0t.obj alles ab und ohne c0t.obj erhalte ich natürlich weiterhin
Error: Undefined symbol _setvect in module mydevice.cAber generell bleibt zudem bei Einführung von “interrupt“ immer das
Error: Undefined symbol DGROUP@ in module mydevice.c ...
-
Was ist denn an deinem Treiber anders als an dem Programm, um das es die ganze Zeit ging? Versteh gar nicht, wieso du schon wieder versuchst, c0t.obj und «interrupt» wegzulassen.
-
c0t hatte ich erst einmal testhalber weggelassen, weil der PC sonst schon beim laden des Treibers abstürzt.
Und “interrupt“ habe ich eben wegen "Undefined symbol DGROUP@" weggelassen.Ein Devicetreiber ist dabei schon etwas ganz, ganz anderes als ein .com-File. Beim Laden des Treibers ist DOS erst unvollständig geladen, weshalb fast alle DOS-Funktionen tabu sind. So etwas wie z.B. printf geht da überhaupt nicht, weil printf DOS-Interrups zur Zeichenausgabe benutzt. Gehe auch davon aus, daß auch setvect nicht gehen wird.
Wenn es mir gelänge (in Assembler wird das ja auch ohne “interrupt“ gemacht) ohne das BCC-eigene setvect und ohne das “interrupt“ auszukommen, wäre das am Sichersten, weil das auf jeden Fall klappen würde (werde also setvect und “interrupt“ erst einmal in meinem RS485-Programm ändern).Bloß, wenn ich mir das Assemlerlisting für die Interruptfunktion ansehe, so sehe ich z.B. neben den üblichen push’s und pop’s der Register noch unverständliches:
push bp
mov bp,cs:DGROUP@
mov ds,bp
mov bp,sp
sub sp,2
Und am Ende der Funktion
mov sp,bp; pop bp; pop di; usw.
Wobei ich nicht verstehe, warum der Stackpointer nach bp muß und warm er danach um 2 reduziert wird und was das Ganze soll und ob es per asm-Direktive in C übernommen werden muß/kann um “interrupt“ zu ersetzen...
-
user4711 schrieb:
c0t hatte ich erst einmal testhalber weggelassen, weil der PC sonst schon beim laden des Treibers abstürzt.
Dann versuch herauszufinden, wo genau es abstürzt.
Und “interrupt“ habe ich eben wegen "Undefined symbol DGROUP@" weggelassen.
Ebenfalls.
Ein Devicetreiber ist dabei schon etwas ganz, ganz anderes als ein .com-File.
Du willst gar kein com-File? Ich dachte dein Treiber soll in der autoexec.bat gestartet werden. Du willst ihn in der config.sys schon einbinden? OK das ändert den Sachverhalt ein wenig.
Beim Laden des Treibers ist DOS erst unvollständig geladen, weshalb fast alle DOS-Funktionen tabu sind. So etwas wie z.B. printf geht da überhaupt nicht, weil printf DOS-Interrups zur Zeichenausgabe benutzt. Gehe auch davon aus, daß auch setvect nicht gehen wird.
Logisch, zum Glück ist setvect fast trivial selbst zu implementieren.
Könnte irgendwie so aussehen:
#include <dos.h> void my_setvect(unsigned intnr, void interrupt (*int_handler)()) { void interrupt (**irq_table)() = MK_FP(0, 0); _disable(); // Interrupts abschalten irq_table[intnr] = int_handler(); _enable(); }
Vielleicht auch so (bin mir nicht sicher, ob Funktionspointer in C genauso wie in der Interrupthandlertabelle codiert sind):
#include <dos.h> void my_setvect(unsigned intnr, void interrupt (*int_handler)()) { unsigned *irq_table = MK_FP(0, 0); _disable(); irq_table[2*intnr] = FP_SEG(int_handler); irq_table[2*intnr+1] = FP_OFF(int_handler); _enable(); }
Guck in der Hilfe nach, ob die FP_-Makros stimmen.
Wenn es mir gelänge (in Assembler wird das ja auch ohne “interrupt“ gemacht)
Tja, warum gehts denn in Assembler ohne "interrupt"? Weil du den Stack von Hand aufsetzen und abbauen musst sowie das IRET selbst codierst. Das geht in C aber nicht, deshalb ist da "interrupt" notwendig.
Bloß, wenn ich mir das Assemlerlisting für die Interruptfunktion ansehe, so sehe ich z.B. neben den üblichen push’s und pop’s der Register noch unverständliches:
push bp
mov bp,cs:DGROUP@
mov ds,bp
mov bp,sp
sub sp,2
Und am Ende der Funktion
mov sp,bp; pop bp; pop di; usw.
Wobei ich nicht verstehe, warum der Stackpointer nach bp muß und warm er danach um 2 reduziert wird und was das Ganze soll und ob es per asm-Direktive in C übernommen werden muß/kann um “interrupt“ zu ersetzen...Da wird ein Stackframe aufgesetzt, zwischendurch wird noch das Datensegment gesetzt. Normalerweise sieht das so aus:
push bp mov bp, sp sub sp, X ...
bp ist die Basisadresse des aktiven Stackframes, über bp werden die lokalen Variablen angesprochen, die zwischen bp und sp (bei sp findet die Stackaktivität, also push/pop/call/ret statt). Der Platz für die Variablen wird durch das Abziehen einer bestimmten Bytezahl von sp (in deinem Beispiel 2) bereit gestellt. In der Interrupt-Prozedur wird jetzt noch das Datensegment aus einer im Codesegment abgelegten Konstanten DGROUP@ gesetzt. Die Frage ist, ob man da ohne weiteres vom Inline-Assembler aus rankommt.
-
Habe die beiden Funktionen zwar getestet, doch bei der ersten Funktion
irq_table[intnr] = int_handler();
bekomme ich zunächst “not allowed here“
aber auch mit einer Änderung in der Form
irq_table[intnr] = int_handler;
oder mit dem zweiten Beispiel erhalte ich da keinen Interrupt installiert (wenn auch sonst nichts abstürzt).Ich hatte mich aber, da ich “interrupt“ ja eh nicht gelinkt bekomme (undefined DGROUP@) inzwischen eigentlich auch dazu entscheiden, eine Interruptfunktion ohne “interrupt“ zu schreiben und sie ohne setvect zu ersetzen, was sicherer ist, denn das muß (irgendwie) auf jeden Fall gehen (aber wie?).
Denn mit “interrupt“ definiert der Compiler immer ein:
_TEXT segment byte public 'CODE'
extrn DGROUP@:word
_TEXT ends
das dann in die Interruptfunktion zu Beginn mit
mov bp,cs:DGROUP@
mov ds,bp
mov bp,sp
sub sp,2
eingebaut wird und beim Linken natürlich fehlt.
Was dieses DGROUP ist (Segment oder Offset) und wo es liegt ist mir dabei nicht klar. Es taucht auch noch in Formen wie
DGROUP group _DATA,_BSS
assume cs:_TEXT,ds:DGROUP
und
mov dx,word ptr DGROUP:_SERIALPORT
auf.Eine normale void-Funktion bekomme ich andererseits leider nicht so einfach als “interrupt“ um-gecastet.