X86 Assembler...

  • Ich habe den Source jetzt noch als COM-File geschrieben ohne Stackdefinition. Als COM-File hat die Datei dann nur 288 bytes. Die große Frage ist, wo befindet sich der Stack ?! Wird der Stacktop automatisch im aktiven Segment an FFFFh gesetzt ?


    Vielen Dank !


    Gruß Jan

  • Bei COM Dateien, wenn nicht explizit angegeben, ist Code-Segment, Daten-Segment und Stack-Segment immer das Gleiche.

    Das sieht bei EXE-Dateien natürlich ganz anders aus.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Ergänzend - der Stack Pointer wird innerhalb des 64KB Segments bei COM-Dateien auf FFFE gesetzt, also möglichst weit weg vom Programm-Start, der bei COM-Dateien fest auf IP = 0100h steht. Bei EXE-Dateien kann das alles anders gesetzt werden, je nachdem was man im Header der Assemblerdatei so definiert. Üblicherweise setzt man auch den Anfang der Daten ans Ende des Codes, also bspw. Zeichenketten usw., aber das ist nirgends fest vorgeschrieben... manche setzen Ihre Variablen auch direkt an den Anfang, nach einem JMP-Befehl, der den Datenbereich erstmal überspringt.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Ja, so langsam rutscht der Groschen... 😊 Das mit dem Stack hatte ich mir schon im debugger angeschaut. Natürlich fffeh und nicht ffffh...ist ja 16 Bit. Mit „t“, also single step, konnte ich dann beibachten, wie der Stackpointer runter bzw. wieder hochgeht bei meinen push und pop instructions..


    Ich hab mir jetzt im Appstore den 8086 Simulator von Antony Mathew runtergeladen für mein Iphone. Man kann zwar keine Codes selber darin schreiben, aber das komplette instruction set ist drin mit Erklärung und Beispielcodes, die man in dem Simulator durch „singlesteppen“ kann. Man sieht dann, wie die Register geschrieben werden und welche Flags wann gesetzt werden.



    Gruss Jan

  • ... ich denke, Dein Stapelspeicher ist etwas klein:

    dw 2 dup(1)

    bedeutet 2 mal das Wort 10, nicht 10 mal das Wort 2.

    Du hast also nur 32 Bytes Stapel - nicht viel.

    Vermutlich geht es gut, aber Du kannst mit dem Debugger gerne mal nachsehen, was am Ende des Programms in Stack steht, d.h. was davon überhaupt genutzt und damit überschrieben wird.

    Deshalb habe ich in meinem Beispiel den Stapelspeicherbereich mit einem gut lesbaren Text vorbelegt (das muss man nicht, kann man auch dup(?) undefiniert lassen, dann müsste die .EXE Datei etwas kleiner werden).


    Solche Stapelüberläufe sind ganz typische Fehler, die dann zu "mal geht es, mal geht es nicht - muss an DOS/Windows/Linux/... liegen" führen.

    Viele Compiler bieten dazu eine Überprüfung an ("Stack Probes"), da wird bei jeder aufgerufenen Unterroutine geprüft, ob der Stapel schon voll ist. Beim Assembler muss man das selbst machen bzw. testen.

  • Ja, also irgendwie komme ich mit dem Stack noch nicht so recht klar. Das COMSET-Programm von oben ist ja fertig und macht auf dem Cordata PPC was es soll. Ich bin aber jetzt an einem neuen Programm. IMGDISK von Dave Dunfield kann ich zB nutzen, um interne Floppys zu justieren. Also nur Laufwerk A: und B:. An meinen IBM Rechnern sind allerdings 37pol Schnittstellen für externe Floppys. Diese nutze ich zwecks Dateienübertragung mittels Gotek. Aber ich würde diese auch gerne zur Instandsetzung bzw. Justage von Floppys nutzen. Und dazu schreibe ich jetzt ein Programm, in dem man das Laufwerk auswählen kann und die Tracks gezielt anfahren kann. Ich habe heute damit angefangen, habe allerdings noch Probleme, vermutlich mit dem Stack..


    Hier mal der Code:


    Es lässt sich assemblieren und auch linken, es läuft also. Nach dem Start wird der Bildschirm gelöscht und der Rechner schaltet auf 40x25 Zeichen Auflösung. Dann wird das Menü dargestellt. Nach den Tasten 1,2,3 oder 4 springt es an die dementsprechenden Subroutines, von denen nur die "Programmende"-Routine bis jetzt fertig ist. Es reagieren auch nur die Tasten 1,2,3 oder 4. Drücke ich 1,2 oder 3 springt er in die Unterroutine und dann wieder zurück, das sieht man an einem kurzen blinken des Menüs.

    Drücke ich jetzt aber 5--6 Mal zB die Taste 3 hintereinander, dann stürzt der Rechner ab. Das kann ja fast nur was mit dem stack zu tun haben ?! Oder was meint ihr ?

    Ich hab natürlich mehrere Stack Definitionen ausprobiert, auch die von Martin. Leider ohne Erfolg. Ist es denn nicht so, dass er einfach irgendwie das ganze Stacksegment als stack nutzen kann ?



    Vielen Dank !


    Gruß Jan

  • naja... RET geht nur wenn es vorher eine Rücksprungadresse auf den Stack gab. JZ macht das nicht.

    So wie Du das gemacht hast, brauchst Du sowieso kein RET sondern ein JMP WFKEY.


    Mit RET ohne JSR davor wird irgendein Zufallswert vom Stack geholt - Crash...

  • Achso ja, JUMP speichert keine Rücksprungadresse auf dem Stack, aber CALL speichert die Rücksprungadresse. Also könnte ich ja auch mit CZ (call if zero) arbeiten, wenn ich "unbedingt" RET am Ende der Routine haben will ?!


    EDIT: Also CZ gibts beim 8088 nicht... Ich bin normalerweise 8080 gewöhnt... :)



    Vielen Dank !


    Gruß Jan

  • jein. Dann kommt das Programm ja an den Befehl nach dem CZ zurück. Sicher auch nicht Dein Plan...


    EDIT: Du machst doch hier sowas wie ein CASE statement in C. Da werden vom Kompiler auch nur JUMPs erzeugt.

  • Also ich habe es jetzt auf JMP geändert und es funktioniert natürlich. Wenn die Routinen laufen, muß ich das wieder abändern, dass er dann wieder das Menü neu auflistet. Stack Definition passt soweit ?


    Gruß Jan

  • Mache den Stack aber größer als gerade ausreichend!

    Ob der SP initialisiert werden muss oder ob dies das DOS macht weiss ich nicht, da müssen Dir andere weiterhelfen!


    Zum Menü wieder auflisten kannste ja einfach nach START springen.

  • Verstehe nicht, warum ihr da beim Stack so knausern müsst. Normalerweise kann man den Stack Pointer einfach auf das Ende des Datensegments stellen, zumindest wenn man nicht 64KB Daten nutzt (und das ist überwiegend wohl nicht der Fall). Da extra Bytes zu reservieren, und vor allem so wenig, verstehe ich nicht....

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

    Einmal editiert, zuletzt von Peter z80.eu ()

  • Normalerweise würde man einen Stack von 256 oder mehr Bytes vorsehen. Auch wenn ein PC nur 640KB hat, sollte es auf die paar Bytes nicht ankommen. Bei einem Microcontroller mit vielleicht nur ein paar Bytes RAM sieht das natürlich anders aus.


    In meinem Beispiel habe ich den Stack mit einer lesbaren Signatur gefüllt.

    Dann setzt man vor dem finalen Ende des Programms einen Breakpoint und kann dann den Stackbereich ansehen. Dann weiß man genau, was man mindestens braucht.

    Den Wert sollte man dann noch sicherheitshalber noch großzügig vergrößern, wenn man den Speicher hat. Außerdem ruft man schnell mehr Unterprogramme mit weiteren PUSH/POP Paaren und RETURN Adressen auf und wenn man dann vergisst, jedes mal den Stack anzupassen kommt man in die Hölle oder an einen anderen dunklen Ort.


    Es kann auch gut sein, dass die aufgerufenen DOS Funktionen den Stack nutzen - viele schalten auf einen eigenen Stack um, aber vermutlich nicht alle.

  • Aber Ihr wisst hoffentlich, dass es sich bei MS-DOS nur um ein Single Thread Single User Betriebssystem handelt, oder ?

    Selbst TSR Programme stören da nicht die Betrachtung, weil sie ganz ans Ende des nutzbaren Speicherbereichs gelegt werden, und somit auch bei der Betrachtung des genutzten Speichers nicht wirklich stören.

    Wenn Ihr also ein MS-DOS Programm startet, gehört Euch der gesamte freie Speicherbereich mehr oder weniger. Da könnt Ihr auch den Stack Pointer einfach innerhalb des freien Bereichs "irgendwo" hinsetzen, da muss nix am Ende des Programmbereichs reserveriert werden, sondern wenn das Programm ein Datensegment nutzt (und das tut es immer!), setzt innerhalb des Datensegments einfach den SP auf FFFE, das passt m.E. nach immer.

    Und das frisst dann kein Byte im Programm selbst.

    Der MASM sorgt eigentlich mit dem Assume Kommando schon dafür, und standardmässig ist der SP bei COM-Dateien, wie bereits erwähnt, schon auf FFFE gesetzt. Kapiere also immer noch nicht eure Anstrengungen...

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • Vielen Dank für die Antworten ! Also grundsätzlich ist es so, dass mein größtes Assemblerprogramm bis dato compiliert 2-3kb hatte (ohne nachladende Sound- und Grafikdateien). Am IBM ein Programm in einem einzigen Segment zu programmieren würde mir sicherlich reichen. Aber mir ging es ja darum, wie die einzigen Segmente "definiert" und angesprochen werden in einem Programm und das habe ich nun verstanden.

    Bei der Programmierung des 6502 hatte ich natürlich nie mit Stack-Definition zu tun, der Stack ist da ja immer zwischen $100 und $1FF. Beim Intel 8080 sieht das etwas anders aus. Da hab ich am Anfang des Programm den alten Stackpointer gerettet, den Stack mit einer Page am Ende des Programms reserviert und am Ende des Programms den alten Stack wieder geholt. Also so:



    Also zurück zum X86. Ich habe mich jetzt dazu entschieden, mit 2 Segmenten zu arbeiten. Dem Code-Segment und dem Daten-Segment. Das ist mehr als ausreichend. Den Stackpointer habe ich (wie oben vorgschlagen) an das Ende des Datensegmentes gelegt. Hier der Code:


    Also das Grundgerüst steht zumindest.... Jetzt muß ich mich durch die Interrupts bzw. Systemcalls durchschlagen, dass das Programm einen Sinn bekommt... :)



    Gruß Jan

    Einmal editiert, zuletzt von Jan1980 ()

  • Und WO setzt Du denn SS:SP auf Datensegment:0xfffe?

    Das musste nun schon selbst machen.

  • Na macht das in dem Fall nicht schon assume in Verbindung mit dem .stack directive im Datensegment ?


    Gruss Jan

  • Ich habe für COM Dateien noch nie den SP explizit gesetzt. Bei EXE Dateien schon eher, aber solche Sachen macht ja MS-DOS bei der Ausführung von Programmen teilweise selbst, ob Du dann das noch explizit auf den gleichen Wert setzt oder nicht, wäre dann egal.

    "The biggest communication problem is we do not listen to understand. We listen to reply." - Stephen Covey


    Webseite und Blog ist immer noch - seit fast 20 Jahren - online.

  • ASSUME macht überhaupt keinen Code.


    Das ist nur eine Direktive für den Assembler, damit er weiss, auf welches Segment sich ein Label bezieht und er dann das richtige Segmentregister dazu verwendet.