Externer Stack in Watcom

Während Windows und Linux interne Funktionen für das Zusammenstellen von Koroutinen anbieten, lässt uns DOS diese Arbeit selber machen.

Zumindest dachte ich das, doch meine Assembler-Künste versagten stets und ließen meine DOSBox Sitzungen sofort einfrieren.

Spoiler: Ich war gar nicht schuld!


Während in einer 64 KByte .com Datei alle 3 Segmente (Code, Data, Stack) in einem zusammenfallen, können .exe Programme im Large-Modus Code und Daten auf mehrere Segmente verteilt werden und die Aufrufe und Zugriffe erfolgen über FAR Calls.

Nur das Stack-Segment bleibt ein und das selbe, weil man sich ohnehin nicht vorstellen konnte, dass jemand mehr als 64 KByte auf den Stack packen wollen würde.

Will ich nun aber ein bisschen kooperatives Multitasking etablieren, so dachte ich, brauche ich einfach nur ein neues Segment von DOS allokieren lassen und über ein paar Assemblerzeilen die Ausführung dort weiterlaufen zu lassen.

Zwar ist es schon etwas schwieriger mit den wenigen 16-bit Registern die notwendigen Daten im Prozessor zu halten um alles korrekt wechseln zu können, doch ich dachte, ich hätte es schon gelöst.
Trotzdem schlugen alle meine Tests mit eingefrorenen Bildschirmen oder Stack-Korruptionen fehl.

Der Compiler muss Bescheid wissen!

Nach langem hin und her suchen, fand ich zum Glück den entscheidenden Eintrag in der OpenWatcom Compiler-Doku.

zu: do not assume that SS contains segment of DGROUP

Im Langtext heißt es:

The zu option relaxes the restriction that the SS register contains the base address of the default data segment, “DGROUP”. Normally, all data items are placed into the group “DGROUP” and the SS register contains the base address of this group. When the “zu” option is selected, the SS register is volatile (assumed to point to another segment) and any global data references require loading a segment register such as DS with the base address of “DGROUP”.
(16-bit only) This option is useful when compiling routines that are to be placed in a Dynamic Link Library (DLL) since the SS register points to the stack segment of the calling application upon entry to the function.
The macro __SW_ZU will be predefined if “zu” is selected.

Das Stack-Segment, das wir vom Compiler am Anfang aufgesetzt bekommen, enthält also “mehr” als nur den Stack und wenn wir uns eigene Stacks schaffen, fehlt das und führt zu Folgefehlern.

In CMake musste ich also noch etwas wie:

1set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -zu")
2set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -zu")

oder moderner

1add_compile_options("-zu")

einfügen und schon funktionierten meine Callstack-Manipulationen wie erwartet.

zu verbessert vermutlich nicht gerade die Performance, aber wenn man dafür ein bisschen Koroutinen-Flair abbekommen kann, bin ich bereit da etwas Leistung herzuschenken.

Fazit

Tja, nicht mal C-Compiler kommen ohne Magie aus.
Man würde sich erwarten, dass C Code unabhängig von der Umgebung arbeitet und einfach seine Daten auf den oder vom Callstack schaufelt ohne zu hinterfragen, wo der Stack genau herkommt.
Doch wenn Optimierungen oder versteckte Werte an bestimmten Stellen liegen, können die nicht so leicht “nachgebaut” werden.

Wie man aber sieht, haben die Macher diesen Spezialfall vorgesehen und durch das genannte Compiler-Flag steuerbar gemacht.

Ein hoch auf OpenWatcom
… wenn auch heute teils mit gekreuzten Fingern.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!