2016-06-12 14 views
2

Ich habe eine Funktion mit 3 Referenzargumente, die einige Assembly-Codes enthält. Ich möchte das Ergebnis dieser Funktion in den Variablen R, G, B wie folgt erhalten.Übergabe der Referenzargumente an eine Assembly-Funktion

 void Get_RGB_color(const DWORD &color, uint8_t &R, uint8_t & G, uint8_t & B)  { 

    _asm { 
     push EAX; 
     xor EAX, EAX; 
     mov EAX,color; 
     mov R, AL; 
     mov G, AH; 
     shr EAX, 16; 
     mov B, AL; 
     pop EAX; 
    } 
    } 

zum Beispiel benutzte ich die Funktion als

DWORD color=RGB(76,0,0); 
uint8_t R, G, B; 
Get_RGB_color(color , R ,G ,B); 

Die Codes haben zwei Probleme:

1- Erste falschen Wert in EAX, in Linie mov EAX, Farbe;

2- Error 'Operandengrße Konflikt' in Zeilen

mov R, AL; mov G, AH; mov B, AL;

Bitte helfen Sie mir

+1

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

+0

Ich denke, ich bekomme die Adresse von Variablen, Wie kann ich den Wert der Variablen erhalten? – SaeidMo7

+0

Ich würde verstärken, was Jester bereits gesagt hat. Glauben Sie, dass diese Assembly schneller ist als Compiler generierter Code? –

Antwort

7
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;/**/ 
+0

Vielen Dank für Ihre Hilfe. – SaeidMo7

+2

@ SaeidMo7: Wenn diese Antwort Ihnen geholfen und Ihr Problem gelöst hat, sollten Sie in Betracht ziehen, die Antwort zu akzeptieren. Weitere Informationen zum Wie und Warum der Annahme einer Antwort finden Sie hier: http://meta.stackexchange.com/a/5235/271768. Ich habe auch bemerkt, dass du nie Antworten auf Antworten angenommen hast, die für dich funktionierten. Du scheinst neu zu sein, und ich würde vorschlagen, deine anderen Fragen zu beantworten und alle Antworten zu akzeptieren, die dein Problem gelöst haben. Willkommen bei Stackoverflow! –