Ergebnis von CMP / Vergleichswerte pushen



  • Hallo Zusammen!

    Ich bin x86-Anfänger und hab eine Frage, und zwar möchte ich das Ergebnis eines CMPs auf den Stack pushen, und zwar so ähnlich wie es in C ist: 0 == falsch, != 0 == true.

    ;Vergleichwerte stehen in eax und ebx
            cmp eax, ebx
            jne nein
            ;True
            mov eax, 1
            push eax
            jmp weiter
    nein:   mov eax, 0
            push eax
    weiter:
    

    nun ist das gräßlich gelöst. Da CMP ja eigentlich ein SUB macht, kam dann als "Verbesserung" sowas raus:

    ;Vergleichwerte stehen in eax und ebx
            sub eax, ebx
            push eax
    

    und wenn ich ein Vergleich auf Ungleichheit machen will

    ;Vergleichwerte stehen in eax und ebx
            sub eax, ebx
            not eax
            push eax
    

    Gut. Um nun das Ergebnis auf gleich oder ungleich zu prüfen könnte ich ein einfaches

    pop eax
    ;Ich bin mir nicht sicher ob CMP einen konstanten Operanden erlaubt...
    cmp eax, 0
    jz wenn_null
    ;sonst true
    .
    .
    .
    jmp weiter
    wenn_null:
    .
    .
    .
    weiter:
    

    machen. Aber wie ist es nun bei den Vergleichen größer-als, kleiner-als, größer-gleich und kleiner-gleich? Ich glaube, ich denke einfach falsch hier, das müsste doch Pisseinfach sein. Habs nicht so mit der Mathematik...

    Jemand ne Idee?



  • Wie du schon richtig erkannt hast, macht cmp eigentlich nur ein sub. Von daher ist deine Idee, das Ergebnis dieser Subtraktion zu sichern schon mal nicht schlecht. Wie sich wohl leicht erkennen lassen sollte, hast du dann bei sub x, y als Ergebnis
    0 fuer x=y
    <0 fuer x<y

    0 fuer x>y
    Diese Eigenschaften lassen sich dann entweder mit einem Vergleich cmp mit 0 (ja, cmp nimmt auch Konstanten und Speicherzugriffe - nur nicht jeweils 2 von der Sorte 😉 ) ueberpruefen, oder in einfachen Faellen (gleich/ungleich 0, < oder < 0 o.Ae.) eleganter mit dem Befehl test und dem Register, das das Ergebnis enthaelt, 2 mal als Operanden. zB. test eax, eax.
    Beide Befehle setzen die Flags dann entsprechend, so dass du mit den entsprechenden Sprungbefehlen je, js, jb, usw. reagieren kannst. Macht sich uebrigens viel besser, die verschiedenen auch negierten Formen der Sprungbefehle zu benutzen, als das zu pruefende Ergebnis vorher zu negieren (uebrigens wenn dann mit neg, nicht mit not, wobei aber neg 0=0 bleibt).

    Ich weiss allerdings nicht so ganz, wozu du das so ueberhaupt brauchst (mir ist das in meinen mindestens 5 Jahren Assembler noch nicht passiert).
    Was du so machst ist nichts anderes als deine 2 Vergleichswerte relativ zu 0 zu setzen und den eigentlichen Vergleich dann spaeter zu machen. Da du wenn die Werte fuer den Vergleich spaeter wirklich nicht mehr verfuegbar sein sollten per pushf/popf auch direkt das Ergebnis des Vergleichs sichern kannst, erscheint das recht unpraktisch.

    zB.:

    cmp eax, ebx    ; Vergleich
    pushfd           ; Die als Ergebnis veraenderten Flags auf den Stack sichern
    ;... irgendwas anderes, das die Flags moeglicherweise veraendert ...
    
    popfd           ; flags wiederherstellen
    je .gleich     ; irgendein auf das cmp reagierender Sprung
    


  • Hallo Nobuo T,

    super erstmal vielen Dank für deine Hilfe. Naja wofür ich das Brauche: ich versuche grad einen Compiler, der in x86 Code compiliert zu schreiben - wäre natürlich wenn ich Assembler schon könnte deutlich einfacher. Was ich hier versuche ist, eine Stackmaschine zu simlieren, also

    push 10
    push 20
    equ
    

    soll mir quasi ein

    mov eax, 10
    push eax
    mov eax, 20
    push eax
    pop eax
    pop ebx
    sub eax, ebx
    ;Hier liegt nun die Schwierigkeit: Der Vergleich kann nur 0 (false) oder nicht 0 (true) sein
    ;also gilt das auch für Größer-gleich, Kleiner-gleich usw.
    push eax
    ;Hier wäre es ja einfach: Wenn eax != 0 ist, dann trifft die Bedingung gleich nicht zu
    

    liefern, in x86 assembly.

    Bei > und < allerdings hab ich ja die Problematik, das ein Wert größer 0 bzw. kleiner 0 rauskommt. Beispiel: 10 < 15. Wenn mein Programm nun ein SUB macht, bekomme ich als Ergebis -5 raus. -5 ist quasi false. Für False sollte er aber dann eine 0 pushen, keine -5. Bei einem Wert >0 ist es ja egal, denn dann ist der Vergleich ja korrekt. Was ich nun aber nicht unbedingt tun will ist, das Ergbenis auf kleiner-null zu prüfen, irgendwo hinzuspringen und dann eine 0 reinschreiben. Das geht dich sicherlich auch mit irgendeinem Trick würd ich mal sagen.

    Bitte hau mir den Code von oben nicht um die Ohren. Ich weis, das mein Compiler keinen effizienten oder in irgendeiner Weise effektiven Code erzeugt. Es ist nur eine Übung, um überhaupt mal ein wenig in Assembler zu machen - denn sonst fehlt mir dazu ehrlich gesagt der Elan.

    Was ich auch mal nett fände wäre, wenn man mir ein gutes (!) Emulationsprogramm für x86 Assembly empfehlen könnte, ähnlich dem Sharewareprogramm emu8086, welches aber unter Linux läuft und ähnlich intuitiv ist.

    Schöne Grüße
    Der Fragemann 😉



  • Herr Fragemann schrieb:

    Was ich nun aber nicht unbedingt tun will ist, das Ergbenis auf kleiner-null zu prüfen, irgendwo hinzuspringen und dann eine 0 reinschreiben. Das geht dich sicherlich auch mit irgendeinem Trick würd ich mal sagen.

    Mal davon ab, dass ich dein Problem damit immer noch nicht ganz verstehe (es wird dir fuer deinen Compiler doch sicher moeglich sein, true und false aehnlich wie zB. in C zu definieren: ungleich und gleich 0), waere das wahrscheinlich aber der uebersihtlichste Weg und da kurze Spruenge immer relativ sind, sehe ich da auch nicht unbedingt eine Schwierigkeit. Notfalls koenntest du so eine Funktion sogar in eigene Prozeduren (Aufruf mit call) verpacken, das waere noch uebersichtlicher.
    Ansonsten hast du noch Befehle wie conditional moves (cmov), die funktionieren aber AFAIR erst ab 686 (Pentium-Pro-kompatibel) und nicht mit aelteren Assemblern oder den setcc-Befehl, der ab 386 funktioniert. Letzterer setzt je nach angegebenen conditions (anstelle der cc, also zB. setnb eax) den Operanden auf 0, falls die Bedingung nicht passt oder 1.

    Herr Fragemann schrieb:

    Bitte hau mir den Code von oben nicht um die Ohren.

    Ich geb mir Muehe, ganz kann ich es aber nicht lassen. 😃

    Herr Fragemann schrieb:

    Was ich auch mal nett fände wäre, wenn man mir ein gutes (!) Emulationsprogramm für x86 Assembly empfehlen könnte, ähnlich dem Sharewareprogramm emu8086, welches aber unter Linux läuft und ähnlich intuitiv ist.

    Kenne ich keines. Bochs ist ein Emulationsprogramm, das auch mit Debugger etc. unter Linux laeuft. Wie das aber meist bei solcher Freeware so ist, kommst du da um massives selber frickeln, um das benutzbar zu machen, AFAIK auch immer noch nicht herum. Mit intuitiv ist da also nicht viel.



  • Hallo Nobuo T

    Danke für die Antwort. Hmm ich will erstmal Versuchen keine Anweisungen >80386 zu benutzen. Also was mein Compiler momentan für ein GREATER erzeugt, also ein

    x > y

    wäre dann jetzt (hier die Original-Ausgabe des Compilers ;))

    ;GREATER
            pop ebx
            pop eax
            cmp eax,ebx
            ja L0
            mov eax, 0
            jmp L1
    L0:     mov eax, 1
    L1:     push eax
    

    Ich kann mit selben Code-Part natürlich auch ein

    x < y

    simulieren, indem ich die beiden pops am Anfang umkehre.
    Du sagtest ja

    Nobuo T schrieb:

    waere das wahrscheinlich aber der uebersihtlichste Weg und da kurze Spruenge immer relativ sind, sehe ich da auch nicht unbedingt eine Schwierigkeit.

    aber irgendwie kann ich nicht glauben, das dies eine schlanke Lösung ist ;). Ach scheiß drauf. Ich strebe immer nach Perfektion, anstatt mal Klein anzufangen :).

    Danke erstmal! 🙂



  • cmp eax,ebx
            ja L0
            mov eax, 0
            jmp L1
    L0:     mov eax, 1
    L1:
    

    Wie's denn so?

    xor ecx,ecx  ; ecx=0
    sub ebx,eax  ; ueberlauf (carry flag gesetzt) wenn eax > ebx
    adc ecx,0    ; carry zu ecx addieren
    

    Oder wenn "1" auch einfach ungleich 0 sein darf:

    sub ebx,eax  ; ueberlauf (carry flag gesetzt) wenn eax > ebx
    sbb ecx,ecx  ; = 0 wenn carry nicht gesetzt, sonst -1
    


  • was macht denn emu8086?

    Warum sollte man auf einem x86 rechner einen x86 emulieren?
    (Mal abgesehen von Virtualisierung)



  • Das Teil emuliert AFAIK nicht irgendeine x86 CPU, sondern einen 8086. Wie auch immer, es kann durchaus sinn machen, einen x86 auf einem x86 zu emulieren. zB. zur Virtualisierung, wie du schon sagtest (ist halt unpraktisch jedes Mal den Rechner neu zu starten und laufend echte Disketten fuer die OS-Entwicklung zu zerstoeren, o.Ae.) oder auch zum besseren Debuggen, auch von Programmen die CPU-Erweiterungen nutzen, die der echte x86 auf dem emuliert wird evtl. gar nicht unterstuetzt, oder sonstige Hardware, etc. usw...


Anmelden zum Antworten