Assembler: Vergleiche & Sprünge

Pascal, Basic und andere nicht aufgelistete
Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Assembler: Vergleiche & Sprünge

Beitrag von Architekt » Sa Jun 28, 2014 3:20 pm

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?

nufan
Wiki-Moderator
Beiträge: 2448
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von nufan » Sa Jun 28, 2014 3:35 pm

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).

oenone
Beiträge: 223
Registriert: Do Sep 01, 2011 2:42 pm
Wohnort: Bremen

Re: Assembler: Vergleiche & Sprünge

Beitrag von oenone » Sa Jun 28, 2014 4:17 pm

Jepp, so macht man das üblicherweise.

Und man kann es durchaus als "Hack" bezeichnen, wenn man Assembler-Programmierung auch als "Hack" sieht :P

Schließlich kommt der Begriff "Hacken" auch vom Programmieren.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von Architekt » Sa Jun 28, 2014 7:55 pm

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.

nufan
Wiki-Moderator
Beiträge: 2448
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von nufan » Sa Jun 28, 2014 10:52 pm

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.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von Architekt » So Jul 06, 2014 10:13 pm

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.
Zuletzt geändert von Architekt am So Jul 06, 2014 10:32 pm, insgesamt 2-mal geändert.

nufan
Wiki-Moderator
Beiträge: 2448
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von nufan » So Jul 06, 2014 10:24 pm

Bitte füg deinem Code Kommentare hinzu die beschreiben was du dir erwartest und was stattdessen passiert.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von Architekt » So Jul 06, 2014 10:29 pm

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. :D

nufan
Wiki-Moderator
Beiträge: 2448
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von nufan » So Jul 06, 2014 10:58 pm

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. :D
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.

Benutzeravatar
Architekt
Beiträge: 172
Registriert: Sa Mai 24, 2014 12:04 pm

Re: Assembler: Vergleiche & Sprünge

Beitrag von Architekt » So Jul 06, 2014 11:07 pm

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. ;)

Antworten