Was tut dieser Assembly Code? (Anfängerfrage)
-
Mal ne Frage am Rande: Muss man diese Prozentzeichen nutzen? Hier in einem Beispiel für InlineAssembler unter VisualStudio sieht das schon sehr viel lesbarer aus.
// Power2_inline_asm.c // compile with: /EHsc // processor: x86 #include <stdio.h> int power2( int num, int power ); int main( void ) { printf_s( "3 times 2 to the power of 5 is %d\n", \ power2( 3, 5) ); } int power2( int num, int power ) { __asm { mov eax, num ; Get first argument mov ecx, power ; Get second argument shl eax, cl ; EAX = EAX * ( 2 to the power of CL ) } // Return with result in EAX }
Quelle:Link Text
-
@chris4cpp sagte in Was tut dieser Assembly Code? (Anfängerfrage):
Mal ne Frage am Rande: Muss man diese Prozentzeichen nutzen?
Lies meinen Text oben.
Intel Syntax: ohne % für Register und ohne $ für Immediates und ohne Längenangabe, dabei bedeutet
mov eax, 0
dasselbe wieeax = 0;
AT&T Syntax: mit % und $. Derselbe Befehl istmovl $0, %eax
- also auch noch mit verdrehten Operanden.Offensichtlich verwendest du in VS Intel Syntax.
(PS: und ja, ich weiß, dass man xor nehmen würde, um ein Register auf 0 zu setzen)
-
Danke noch mal, ja sorry nicht gelesen.
PS: Na ich vermute mal, solche Kniffe wie mit xor brauch man heute nicht mehr. Und Speicher soll wohl auch ein ziemlicher Flaschenhals geworden sein, da dessen Geschwindigkeit nicht so schnell gewachsen ist wie bei den CPUs, also wird es heute wohl weniger LUTs geben als früher, wenn die Berechnung weniger kostet als ein Speicherzugriff. Aber in der Materie bin ich nicht mehr drin.
-
Der Opcode für
xor eax, eax
ist kürzer als der fürmov eax, 0
, daher verwendet man es immer noch. Ich würde sogar sagen dass es so üblich ist, dass es den einen oder anderen geübten Assembler-Leser ein klein bisschen verwirren könntemov eax, 0
zu lesen.BTW:
xor eax, eax
ist auch kürzer alsxor rax, rax
, weswegen manxor eax, eax
auch findet wenn das ganzerax
auf 0 gesetzt werden soll. Was sich wieder die Tatsache zu nutze macht dass bei 32 Bit Befehlen im 64 Bit Mode die oberen 32 Bit des Ziel-Registers auf 0 gesetzt werden.Und Speicher soll wohl auch ein ziemlicher Flaschenhals geworden sein, da dessen Geschwindigkeit nicht so schnell gewachsen ist wie bei den CPUs, also wird es heute wohl weniger LUTs geben als früher, wenn die Berechnung weniger kostet als ein Speicherzugriff.
Was günstiger ist kommt auf viele Faktoren an. Wenn man viele Werte in Folge aus der LUT braucht, die LUT klein genug ist und die Befehle um den Wert direkt zu berechnen teuer genug, dann zahlt sich das schon noch aus. Für eine Multiplikation macht man es aber eher nicht mehr. Für eine Division meist auch nicht, aber für viele in Folge... vielleicht doch.
-
Hier kommt immer eine Gleitkomma-Ausnahme und ich weiß auch nicht, wieso ich
rax
nur relativ adressieren kann:.text .globl getx .type getx, @function getx: .LFB0: push %rax push %rbx push %rcx push %rdx # mov %r8, %rax # mov %r9, %rbx # main.c:7: float w2 = w * (M_PI / 180.0); # main.c:8: x = (int) (r * cos(w2)); # main.c:9: return x; mov $0x4048f5c3, %rax mov $0x43340000, %rbx div %rbx mul %r8 movsd 0(%rax), %xmm0 call cos@PLT movsd %xmm0, 0(%rax) mul %r9 mov 0(%rax), %eax # pop %rax # pop %rbx # pop %rcx # pop %rdx leave ret
Aufruf:
# main.c:24: x = getx(25, i); mov %rbp, %r8 # i, tmp87 mov $25, %r9 #, call getx # movl %eax, -16(%rbp) # tmp88, x
-
Die Ausnahme kommt vermutlich gerade weil du rax "relativ addressierst" - das heisst ja du lädst den Wert für xmm0 aus dem Speicher, und zwar von der Adresse die in rax steht. Und da an der Adresse - wenig überraschend - nix gemappt ist, gibt's aua.
Und warum es nicht "direkt" geht: rax ist ein 64 Bit Skalar-Register, xmm0 ist ein 128 Bit Vektorregister. Wie soll das also gehen? Angenommen es gibt einen Register<->Register mov Befehl der das kann (keinen Ahnung ob es den gibt), dann müsstest du zumindest irgendwo mit angeben was genau er jetzt mit den 64 Bit machen soll. Also wie er die 64 Bit aus rax auf xmm0 "aufteilen" soll.
Bei "indirekt" dagegen nimmt der Compiler an dass xmm0 in seinem normalen Format (also 128 Bit) im Speicher liegt. Und das geht natürlich immer, dabei ist ja keine Konvertierung/Abbildung von Bits nötig.
-
Auch damit geht es nicht (gleiche Fehlermeldung)
getx: .LFB0: push %rax push %rbx push %rcx push %rdx # mov %r8, %rax # mov %r9, %rbx # main.c:7: float w2 = w * (M_PI / 180.0); # main.c:8: x = (int) (r * cos(w2)); # main.c:9: return x; mov $0x4048f5c3, %rax mov $0x43340000, %rbx div %rbx mul %r8 movd %rax, %xmm0 call cos@PLT movd %xmm0, %rax mul %r9 movd %rax, %xmm0 cvttsd2si %xmm0, %eax # pop %rax # pop %rbx # pop %rcx # pop %rdx leave ret
-
Sorry, ich hab vollkommen übersehen dass da ja nicht
mov
sondernmovd
stand. Peinlich.Fangen wir mal so an: Was willst du denn überhaupt machen?
Und: Ich mag mich jetzt täuschen, aber mir wäre nicht bekannt dass man bei AMD64 die "normalen" Register (rax, rbx, ..., r8 ... r15) für Floating-Point Berechnungen verwenden könnte.
Und z.B.mul %r9
ist ziemlich sicher ne 64 Bit Integer Multiplikation (
rax = rax * r9
). Da würde ich jetzt mittlere Geldbeträge drauf verwetten.Wenn du Float-Zeugs machen willst musst du die SSE* Register verwenden (oder den guten alten x87 Registerstack).
-
Also was ich machen möchte das steht in den Kommentaren in Zeile 9, 10 und 11.
Sowas blödes... Dann hab ich die ganze Zeit die falschen Register verwendet!
Aber nochmal ganz kurz umrissen, was ich machen möchte:
Es werden zwei int Werte übergeben, sagen wir jetzt einfach mal i und j.
Dann möchte ich einmal dividieren und zweimal multiplizieren (und einmal cos aufrufen):
(cos((3.14 / 180.0) * i) * j)
Das Ergebnis möchte ich dann wieder in einen int Wert konvertieren und nach%eax
schieben.
-
@EinNutzer0 (3.14 / 180.0) ist ein Konstanter Wert, den du schon vorher ausrechnen kannst (spart die Division).
-
@DirkB : richtig, das wäre
$0x3c8efa35
.
-
@EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):
Dann möchte ich einmal dividieren und zweimal multiplizieren (und einmal cos aufrufen):
(cos((3.14 / 180.0) * i) * j)Du willst aber eine floating-Point-Division durchführen! div mit rax ist aber Integer-Division. Floating point Division ist fdiv (wie im Pentium-Bug) (das wäre @hustbaer s "guter alter" Weg). Mit SSE kannst du z.B. mit
cvtsi2sd xmm0, eax
den Wert aus eax von int nach double konvertieren. Dann weiter mit divsd oder so.Schau dir doch einfach mal den gewünschten Code an im Compiler Explorer an. Nur für dich als Beispiel: https://godbolt.org/z/7cxXFW
-
@EinNutzer0 ich weiß immer noch nicht was du mit x86-64 asm willst wenn du lernen sollst einen bestimmten (welchen?) µController zu programmieren. Geht nicht in mein Hirn.
-
@wob : super, ich weiß nur noch nicht, wie man selber zu solch einer Lösung gelangen kann.
-
@wob sagte in Was tut dieser Assembly Code? (Anfängerfrage):
dann hast du doch bestimmt irgendwas bekommen haben, womit du lernen sollst?!
Wir haben ein Skript. Das gute Stück umfasst 500 Folien und mehr... Aber das Problem: es ist zwar Assembler Code vorhanden, jedoch wird dieser in keinster Weise erklärt.
-
@EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):
Wir haben ein Skript. Das gute Stück umfasst 500 Folien und mehr... Aber das Problem: es ist zwar Assembler Code vorhanden, jedoch wird dieser in keinster Weise erklärt.
Nochmal:
@Swordfish sagte in Was tut dieser Assembly Code? (Anfängerfrage):
[...] (welchen?) µController [...]
MMX auf x86-64 hilft dir dabei sicher nicht.
-
Ich hab noch lauter Fragezeichen... funktioniert muls.. , divs.. usw. nur dann, wenn ich eax vorher in xmm schiebe? Ist das immer so, dass es mit den general purpose register nicht funktioniert? Und was genau bewirkt
push %rax
undpush %rbx
, wieso braucht man danach einmov
für lokale Variablen? Und soll man nochpop
aufrufen oder bewirkt das dasleave
.Sorry man merkt ich bin Anfänger auf diesem Gebiet, aber haben wir alle nicht mal low begonnen?
@Swordfish sagte in Was tut dieser Assembly Code? (Anfängerfrage):
(welchen?) µController
Das versuche ich gerade herauszufinden... In der Prüfung kann auch ein ganz anderer mc drankommen, es wird ein Teil der Doku mitgegeben. Dennoch möchte ich mich "vorbereiten".
-
Habe jetzt Assemblercode der funktioniert aber das falsche Ergebnis für x ausgibt:
.text .globl getx .type getx, @function getx: .LFB0: push %rcx # main.c:7: float w2 = w * (M_PI / 180.0); # main.c:8: x = (int) (r * cos(w2)); # main.c:9: return x; movl $0x4048f5c3, %ecx movl $0x43340000, %edx cvtsi2sd %ecx, %xmm0 cvtsi2sd %edx, %xmm1 divsd %xmm0, %xmm1 cvtsi2sd %eax, %xmm0 mulsd %xmm0, %xmm1 movsd %xmm1, %xmm0 call cos@PLT movsd %xmm0, %xmm1 cvtsi2sd %ebx, %xmm0 mulsd %xmm0, %xmm1 cvttsd2si %xmm1, %eax pop %rcx ret
Caller:
# main.c:24: x = getx(25, i); movl -4(%rbp), %ebx # i, tmp87 movl $25, %eax #, call getx # movl %eax, -16(%rbp) # tmp88, x # main.c:25: y = gety(25, i); movl -4(%rbp), %eax # i, tmp89 movl %eax, %esi # tmp89, movl $25, %edi #, call gety # movl %eax, -20(%rbp) # tmp90, y
Wieso ist das Ergebnis noch falsch?
-
@EinNutzer0 sagte in Was tut dieser Assembly Code? (Anfängerfrage):
In der Prüfung kann auch ein ganz anderer mc drankommen, es wird ein Teil der Doku mitgegeben.
Dann wird das sicher etwas ziemlich simples sein. Ich finde aber so eine Ansage eher ... ähm ... pervers.
Auf jeden Fall wird es mit ziemlicher Sicherheit nicht AT&T-Syntax sein.
-
Stimme Swordfish zu. Du willst jetzt speziell Floating-Point-Berechnung auf X8664 lernen, um Mikrocontroller-Assembler zu schreiben?!
Jedenfalls: was tust du denn eigentlich in deiner Funktion?
Du teilst 0x43340000 durch 0x4048f5c3 (oder andersrum, ich kommt bei at&t immer durcheinander) - das ergibt 1.0453936 und hat in jedem Fall nichts mit pi/180 zu tun. Wie kommst du überhaupt auf die beiden Zahlen in Zeile 10/11?