push EAX;
Warum Sie EAX drängen hier sind? Das ist nicht nötig. EAX ist ein Anrufer-Speicher-Register, was bedeutet, dass der Angerufene (, d. H., Ihre Funktion) frei ist, es zu überlisten. Sie sind nicht verpflichtet, ihren Wert zu erhalten.
(EAX, EDX und ECX sind die Anrufer-Save-Register in dem Win32-ABI, die anderen sind Rufenen-speichern.)
Der einzige andere Grund, ein Register zu schieben wäre der Stapel auszurichten, aber das ist hier auch nicht nötig. Der Stapel wird bereits korrekt ausgerichtet, wenn die Steuerung an Ihre Funktion übergeben wird.
xor EAX, EAX;
Ich nehme an, Sie wissen, dass dies ein häufig verwendeter Trick ist, ein Register (XOR-Verknüpfung es mit mir selbst) für das Clearing. Sie müssen jedoch ein Register nicht vorher löschen, bevor Sie einen Wert eingeben.
mov EAX,color;
Diese Linie ist falsch; Das ist es, was der Assembler-Fehler Ihnen sagt. color
wird an diese Funktion als ein Verweis-zu-DWORD übergeben, aber unter der Haube, Referenzen sind als Zeiger implementiert, so dass es tatsächlich als ein Zeiger auf DWORD übergeben wird. Das bedeutet, dass Sie nicht direkt auf den Wert der Farbe zugreifen können. Sie müssen Zeigerindirection (oder "indirect addressing" in x86-Sprache) verwenden. Da Sie Inline-Assembly verwenden, können Sie die Compiler lassen Sie den Stapel Buchhaltung tun und nur durch den Namen des formalen Parameters auf die Speicherstelle verweisen:
mov EAX, DWORD PTR [color] ; get the address of color
mov EAX, DWORD PTR [EAX] ; dereference it, storing the result in EAX
Natürlich, da Sie nicht wirklich sind Änderncolor
innerhalb dieser Funktion gibt es keinen Grund, es als Referenzparameter übergeben. Im Allgemeinen sollten Skalarwerte (, z. B., ganze Zahlen) immer durch Wert und nicht durch Verweis übergeben werden, es sei denn, Sie benötigen die Referenz tatsächlich.Dies ist sowohl effizienter als auch lesbarer - ein optimierender Compiler übergibt den Wert in einem Register, wodurch diese Zeigerindirektion und ihre zugehörigen Kosten völlig unnötig werden.
mov R, AL;
Hier wird die Assembler Ihnen einen "Operandengrße Konflikt" Fehler geben. Da R
tatsächlich eine Referenz ist, die als Zeiger implementiert ist, sind es 32 Bits. Es ist ein 32-Bit-Zeiger, der auf eine 8-Bit-Position im Speicher zeigt. Sie versuchen also, einen 8-Bit-Wert (AL) in eine 32-Bit-Position (den Zeiger) zu verschieben. Die Operanden sind unterschiedlich groß. Also noch einmal, Sie müssen indirekte Adressierung verwenden. Es sieht genauso aus wie der Code oben, außer dass jetzt R
ist Byte-Größe und braucht ein anderes Register als Scratch-Register, um den Wert in EAX zu vermeiden, verwendet clobbering wir so hart gearbeitet, um dorthin zu gelangen:
mov EDX, DWORD PTR [R] ; get the address of R
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there
Dadurch wird das niederwertige Byte von EAX (das wir als AL bezeichnen können) in den byte-großen Speicherplatz verschoben, der durch R
festgelegt wird.
Das Gleiche gilt für die nächste Zeile, außer dass Sie jetzt das High Byte von EAX (bezeichnet als AH) verschieben. Wir können EDX Wiederverwendung jetzt hier, weil wir seinen alten Wert nicht wieder brauchen:
mov EDX, DWORD PTR [G] ; get the address of G
mov BYTE PTR [EDX], AH ; dereference it so we can store AH in there
shr EAX, 16;
Das ist richtig.
mov B, AL;
dritte Strophe, die gleiche wie die erste. Wie Sie jetzt wissen, sollte dies sein:
mov EDX, DWORD PTR [B] ; get the address of B
mov BYTE PTR [EDX], AL ; dereference it so we can store AL in there
pop EAX;
Popping EAX jetzt nicht notwendig ist, da wir nicht EAX am Anfang schieben haben.
setzen sie alle zusammen, dann können Sie die folgende Abfolge von Anweisungen erhalten:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
mov BYTE PTR [EDX], AL
mov EDX, DWORD PTR [G]
mov BYTE PTR [EDX], AH
shr EAX, 16
mov EDX, DWORD PTR [B]
mov BYTE PTR [EDX], AL
}
}
Dies ist jedoch nicht der optimalste Weg, um den Code zu schreiben. Der Zugriff auf die niedrigen und hohen 8 Bits eines 32-Bit-Registers ist zwar zulässig, jedoch langsam. Eine Optimierung der Compiler würde dies vermeiden, und in den Prozess, wodurch die Notwendigkeit für eine Schaltanweisung vermeiden:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color] ; get the address of color
mov EAX, DWORD PTR [EAX] ; get the value in EAX
mov EDX, DWORD PTR [R] ; get the address of R
mov CL, BYTE PTR [EAX] ; get the value of the lowest byte (8 bits) of color
mov BYTE PTR [EDX], CL ; dereference R and store that byte in it
mov EDX, DWORD PTR [G] ; get the address of G
mov CL, BYTE PTR [EAX + 1] ; get the value of the second-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference G and store that byte in it
mov EDX, DWORD PTR [B] ; get the address of B
mov CL, BYTE PTR [EAX + 2] ; get the value of the third-to-lowest byte in color
mov BYTE PTR [EDX], CL ; dereference B and store that byte in it
}
}
Aber es gibt noch Teilregister Stände dort lauern Dinge zu verlangsamen. Also ein wirklich intelligenter Compiler würde diejenigen, die durch entweder eliminiert die Register vorgeNullStellung oder mit movzx
:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
__asm
{
mov EAX, DWORD PTR [color]
mov EAX, DWORD PTR [EAX]
mov EDX, DWORD PTR [R]
movzx ECX, BYTE PTR [EAX]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [G]
movzx ECX, BYTE PTR [EAX + 1]
mov BYTE PTR [EDX], CL
mov EDX, DWORD PTR [B]
movzx ECX, BYTE PTR [EAX + 2]
mov BYTE PTR [EDX], CL
}
}
Es könnte auch die Anweisungen neu ordnen und geschickt Register zuordnen, die drei Operationen so weit wie möglich parallelisieren. Es gibt zweifellos noch effizientere Möglichkeiten, dies zu tun.Es sei denn, Sie versuchen, Assembler-Programmierung (in diesem Fall, mit dem Inline-Assembler machen nicht viel Sinn) zu lernen, stark bevorzugen den Code wie folgt zu schreiben:
void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)
{
R = (color & 0xFF);
G = ((color >> 8) & 0xFF);
B = ((color >> 16) & 0xFF);
}
Eine letzte Hinweis: Sie müssen nicht jede Assemblersprache mit einem Semikolon beenden, selbst wenn Sie die Inline-Assembly-Syntax verwenden. Das ist nur in C und C++ notwendig. Der Grund, warum spielt es keine Rolle, obwohl, weil ein Semikolon ist eigentlich ein Kommentar Begrenzer in Assembler, so wäre es wie das Schreiben der folgenden in C:
int foo;/**/
Referenzen werden als Zeiger tatsächlich weitergegeben, was Sie brauchen um sie als solche zu behandeln. PS: Es gibt keinen Grund, die Montage dafür zu verwenden. – Jester
Ich denke, ich bekomme die Adresse von Variablen, Wie kann ich den Wert der Variablen erhalten? – SaeidMo7
Ich würde verstärken, was Jester bereits gesagt hat. Glauben Sie, dass diese Assembly schneller ist als Compiler generierter Code? –