Seite 1 von 2
Assembler: Vergleiche & Sprünge
Verfasst: Sa Jun 28, 2014 3:20 pm
von Architekt
Hallo.
Ich versuche mich gerade an Sprüngen und Vergleichen.
Wie genau vergleicht und springt man am besten?
Zum Beispiel das folgende Problem:
Code: Alles auswählen
.text
.globl _prog
_prog:
push %ebp
movl %esp, %ebp
movl $41, %eax
cmp $40, %eax
je equal
nequal:
dec %eax
equal:
inc %eax
pushl %eax
call _println_int
addl $4, %esp
pop %ebp
ret
Bei not equal würde er zunächst das Label nequal, dann aber doch das Label equal betreten. Die bisherige Lösung: ein weiteres Label:
Code: Alles auswählen
.text
.globl _prog
_prog:
push %ebp
movl %esp, %ebp
movl $41, %eax
cmp $40, %eax
je equal
nequal:
dec %eax
jmp end
equal:
inc %eax
end:
pushl %eax
call _println_int
addl $4, %esp
pop %ebp
ret
So wird equal übersprungen. Doch das sieht für mich eher wie ein Hack aus. Wie könnte man es besser machen?
Re: Assembler: Vergleiche & Sprünge
Verfasst: Sa Jun 28, 2014 3:35 pm
von nufan
Architekt hat geschrieben:Doch das sieht für mich eher wie ein Hack aus. Wie könnte man es besser machen?
Das ist kein Hack, das ist Assembler-Programmierung ^^ Es gibt kein if-else-Konstrukt falls du dir das erwartest (zumindest nicht in GAS).
Re: Assembler: Vergleiche & Sprünge
Verfasst: Sa Jun 28, 2014 4:17 pm
von oenone
Jepp, so macht man das üblicherweise.
Und man kann es durchaus als "Hack" bezeichnen, wenn man Assembler-Programmierung auch als "Hack" sieht
Schließlich kommt der Begriff "Hacken" auch vom Programmieren.
Re: Assembler: Vergleiche & Sprünge
Verfasst: Sa Jun 28, 2014 7:55 pm
von Architekt
Ich dachte eher daran, irgendwie die Labels auszulagern und mit ret zurückzuspringen:
Code: Alles auswählen
.text
.globl _prog
_prog:
push %ebp
movl %esp, %ebp
movl $41, %eax
cmp $40, %eax
je equal
pushl %eax
call _println_int
addl $4, %esp
pop %ebp
ret
nequal:
dec %eax
ret
equal:
inc %eax
ret
Aber das funktioniert leider nicht. Aber mit so einer Lösung bräuchte ich keinen extra Sprung und kein extra Label.
Re: Assembler: Vergleiche & Sprünge
Verfasst: Sa Jun 28, 2014 10:52 pm
von nufan
Das liegt daran, dass ret sich die Rücksprungadresse am Stack erwartet (und du diese nicht auf den Stack legst). Bei Funktionen erledigt call das für dich automatisch. Du kannst also call verwenden und mit ret zurückspringen, die Entscheidung welche Funktion du callst wird dadurch aber nicht leichter oder schöner. Mit GAS kriegst du das leider nicht besser hin, damit musst du leben.
Re: Assembler: Vergleiche & Sprünge
Verfasst: So Jul 06, 2014 10:13 pm
von Architekt
Moin.
Ich hab da noch eine Frage zu.
Kann mir jemand erklären, warum laut Compiler dieser Code nicht true (equal) zurückliefert?
Code: Alles auswählen
.text
.globl _prog
_prog:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $1, 0(%esp) # variable x = 1
pushl $42 # 42 auf den Stack
movl $42, %eax # 42 ins AX Register
cmpl %eax, 0(%esp) # Vergleichen von Register (42) und Stack (42)
addl $4, %esp # Stack bereinigen wegen push
je equal # Wenn equal (42 == 42) springe zu equal
nequal:
subl $1, 0(%esp) # Dekrementieren
jmp end # Ab zum Ende
equal:
addl $1, 0(%esp) # Inkrementieren
end:
pushl 0(%esp) # Variable x pushen
call _println_int # Variable x ausgeben
addl $4, %esp # Stack bereinigen
addl $4, %esp
pop %ebp
ret
pushe ich die 42 nicht, sondern move sie in %ebx und vergleiche damit, geht es.
Re: Assembler: Vergleiche & Sprünge
Verfasst: So Jul 06, 2014 10:24 pm
von nufan
Bitte füg deinem Code Kommentare hinzu die beschreiben was du dir erwartest und was stattdessen passiert.
Re: Assembler: Vergleiche & Sprünge
Verfasst: So Jul 06, 2014 10:29 pm
von Architekt
Ich dachte das wäre aus dem Snippet ersichtlich.
Ich erwarte, dass der Compiler mir Recht gibt, dass 42 == 42 ist. Demnach sollte er in das Label 'equal' springen und dort die Variable in 0(%esp) (die derzeit auf 1 steht) inkrementieren. Stattdessen geht er natürlich ins Label 'nequal' und dekrementiert die Variable. Ich erwarte also eig. die Ausgabe 2 (1 + 1) und nicht 0 (1 - 1). Doch irgendwie ist 42 != 42.
Re: Assembler: Vergleiche & Sprünge
Verfasst: So Jul 06, 2014 10:58 pm
von nufan
Architekt hat geschrieben:Ich dachte das wäre aus dem Snippet ersichtlich.
Ich kann aus dem Code rauslesen was er macht. Aber das scheint ja nicht das gleiche zu sein wie das was du erwartest.
Außerdem erleichterst du mir damit die Arbeit und ersparst mir Zeit - was der Motivation über das eigentliche Problem nachzudenken auch nicht schadet.
Architekt hat geschrieben:Ich erwarte, dass der Compiler mir Recht gibt, dass 42 == 42 ist. Demnach sollte er in das Label 'equal' springen und dort die Variable in 0(%esp) (die derzeit auf 1 steht) inkrementieren. Stattdessen geht er natürlich ins Label 'nequal' und dekrementiert die Variable. Ich erwarte also eig. die Ausgabe 2 (1 + 1) und nicht 0 (1 - 1). Doch irgendwie ist 42 != 42.
Das ist dein Problem:
Code: Alles auswählen
cmpl %eax, 0(%esp) # Vergleichen von Register (42) und Stack (42)
addl $4, %esp # Stack bereinigen wegen push
je equal # Wenn equal (42 == 42) springe zu equal
"cmp" ist auf CPU-Ebene eine Subtraktion, die (wie auch jede andere arithmetische Operation) Bits im Flags-Register setzt. "je" prüft auf Gleichheit, indem es sich den Wert des Zero-Flags im Flag-Register ansieht. Das Zero-Flag ist genau dann gesetzt, wenn die vorhergehende Operation 0 als Ergebnis hatte. Das ist bei einer Subtraktion natürlich genau dann der Fall, wenn die beiden Operanden den gleichen Wert hatten.
Du addierst jedoch zwischen "cmp" und "je" noch 4 zu esp, was die Flags erneut setzt, eben in Bezug auf das "add". Eine Addition zu esp wird in einem gültigen Programm nie 0 ergeben, also wird dein Zero-Flag wieder auf 0 gesetzt und dein "je" nicht ausgeführt.
Kurz: Keine Instruktionen zwischen cmp und jX (je, jg, jz, ...), wenn du dir nicht 100% sicher bist was du tust.
Re: Assembler: Vergleiche & Sprünge
Verfasst: So Jul 06, 2014 11:07 pm
von Architekt
Hm, ein pop hat es in der Tat gelöst. Gibt es da noch eine "schönere" / "bessere" Variante? Oder lieber den Stack vermeiden (sprich kein push) und Register verwenden?
Danke jedenfalls.