Wie wird das Registerflagbei CMPSD gesetzt?
-
Hallo,
ich habe folgenden Code, der zwei Speicherstellen (mit angegebener Größe) vergleicht.
function MemCompare(var lho, rho; size: DWORD): Integer; assembler; asm { EAX -> lho result = 0 equal EDX -> rho result = 1 lho > rho ECS -> size result = -1 lho < rho } push edi push esi push ebx mov edi, eax mov esi, edx add edi, ecx add esi, ecx // offset xor eax, eax // null result mov ebx, ecx // save value shr ecx, 2 // look for dword align jz @@Rest std // set direction flag to decrement repe cmpsd ja @@Above jb @@Below @@Rest: mov ecx, ebx and ecx, 3 repe cmpsb jz @@Exit jb @@Below @@Above: // lho < rho dec eax jmp @@Exit @@Below: // lho > rho inc eax @@Exit: cld pop ebx pop esi pop edi end; (* of MemCompare *)
Leider funktinoiert es nicht ganz. Wenn beide Werte gleich sind, dann gibt er 0 zurück (korrekt), aber ansonsten springt er immer zu below ... warum?
EDIT: Da war ich mal wieder zu hastig. Ist natürlich ein Fehler drin gewesen:
function MemCompare(const lho, rho; size: DWORD): Integer; assembler; asm { EAX -> lho result = 0 equal EDX -> rho result = 1 lho > rho ECS -> size result = -1 lho < rho } push edi push esi push ebx mov edi, eax mov esi, edx xor eax, eax // null result mov ebx, ecx // save value shr ecx, 2 // look for dword align jz @@Rest mov edx, ebx sub edx, 4 add edi, edx // position on last dword add esi, edx { mov eax, dword ptr [esi] mov eax, dword ptr [edi] sub esi, 4 sub edi, 4 mov eax, dword ptr [esi] mov eax, dword ptr [edi] } std // set direction flag to decrement repe cmpsd ja @@Above jb @@Below @@Rest: mov ecx, ebx and ecx, 3 repe cmpsb jz @@Exit jb @@Below @@Above: // lho < rho dec eax jmp @@Exit @@Below: // lho > rho inc eax @@Exit: cld pop ebx pop esi pop edi end; (* of MemCompare *) (* ------------------------------------------------------------------------------------ *)
-
Wieso erfolgt der Vergleich bei den dword-Schritten von hinten, nicht aber bei den Einzelbytevergleichen, wenn size<4 ?
-
Und warum asiatischer Still?
-
camper schrieb:
Wieso erfolgt der Vergleich bei den dword-Schritten von hinten, nicht aber bei den Einzelbytevergleichen, wenn size<4 ?
Ja, den fehler habe ich kurz nach dem posten auch bemerkt. Ist natürlich Quatsch. Gründsätzlich geht es darum, dass ich natürlich das höchstwertige Byte zu erst vergleichen möchte.
@abc.w
asiatischer Stil?
-
FrEEzE2046 schrieb:
@abc.w
asiatischer Stil?Ja, wegen grosser Abstände
mov edi, eax
Und so kann man Deine Funktion "nur" von oben nach unten lesen und das ist unleserlich (für mich)
-
Funktioniert denn so eine einfache Variante?
function MemCompare(const lho, rho; size: DWORD): Integer; assembler; asm { EAX -> lho result = -1 lho < rho EDX -> rho result = 0 lho == rho ECX -> size result = 1 lho > rho } push edi push esi lea esi, [eax+ecx-1] lea edi, [edx+ecx-1] std rep cmpsb cld seta eax cmovb eax, -1 pop esi pop edi end;
-
camper schrieb:
Funktioniert denn so eine einfache Variante?
Es funktioniert auch so eine einfache Variante - wobei mir folgende Zeilen schleierhaft sind:
lea esi, [eax+ecx-1] lea edi, [edx+ecx-1]
Im EAX bzw. EDX Register steht bereits die Adresse des Strings ... mit der Adresse des Pointers selbst kann ich nicht viel anfangen.
Ansonsten ist es halt einfach schneller zunächst die DWORDs zu überprüfen (gerade bei größeren Werten) würde ich sagen.
-
FrEEzE2046 schrieb:
Ansonsten ist es halt einfach schneller zunächst die DWORDs zu überprüfen (gerade bei größeren Werten) würde ich sagen.
Stimmt, allerdings muss der Wert von esi/edi beim Übergang von cmpsd auf cmpsb entsprechend angepasst werden. Da die Fehlerbeschreibung nicht ganz zutreffend ist, ist das bisher vermutlich niemandem aufgefallen:
function MemCompare(const lho, rho; size: DWORD): Integer; assembler; asm { EAX -> lho result = -1 lho < rho EDX -> rho result = 0 lho == rho ECX -> size result = 1 lho > rho } push edi push esi lea esi, [eax+ecx-1] lea edi, [edx+ecx-1] std mov eax, ecx shr eax, 2 and ecx, 3 repe cmpsb jne @@Exit mov ecx, eax lea esi, [esi-3] lea edi, [edi-3] repe cmpsd @@Exit: cld seta eax cmovb eax, -1 pop esi pop edi end;
Da es wahrscheinlicher sein dürfte, dass der Stringanfang dword ausgerichtet ist, als dass das beim Stringende der Fall ist, sollte die byte-weisen Vergleiche zuerst stattfinden, damit die Vergleichsadressen für den dword-Vergleich ausgerichtet sind.
Edit: kleine Änderung, damit es auch mit size<4 klappt (Flags!)
-
camper schrieb:
Da es wahrscheinlicher sein dürfte, dass der Stringanfang dword ausgerichtet ist, als dass das beim Stringende der Fall ist, sollte die byte-weisen Vergleiche zuerst stattfinden, damit die Vergleichsadressen für den dword-Vergleich ausgerichtet sind.
Ich gebe dir recht im Bezug darauf, dass es wahrscheinlicher sein dürfte, dass der Anfang DWORD aligned ist, als das Ende - bei einem String, in diesem Fall jedoch nicht.
Warum du jedoch vom edi und esi Register 3 abziehst verstehe ich nicht.
Erst einmal sollte dir doch auffallen, dass ich von hinten Vergleiche und danach entscheide ob lho oder rho größer, kleiner, gleich ist.
Wenn ich Strings vergleichen würde (also Zeichenketten), dann wäre es ziemlicher Schwachsinn am least significant byte fest zu machen welcher größer ist oderDieser byteweise Vergleich ist vorallem für ganzzahlige Typen gedacht gewesen (oder Vektoren davon). Da wir die Werte in little endian Reihenfolge vorfinden, ist das "hintere" dword eines (z.B.) long long nun mal das signifikantere für die Feststellung größer, kleiner, gleich.
Daher ist es - aus meiner Sicht - auch richtiger "zuerst" die DWORDs zu vergleichen und dann die Bytes.Desweiteren sehe ich kein Problem bei der Verwendung meiens Codes. Nenn bitte ein Beispiel, bei dem der Vergleich "fehlschlagen" würde. Ich will damit nicht sagen, dass du unrecht hast, aber ich sehe es wirklich nicht.
-
Betrache:
lho = "000aaa"
rho = "000aaa"
size = 6
In deinem Code: beim ersten Vergleich betrachtest du die letzten vier Zeichen:000aaa 000aaa ^ esi/edi
Am Ende des Vergleiches werden esi/edi erniedrigt um die Größe des Wergleichswortes, also 4:
000aaa 000aaa ^ esi/edi
Der folgende byte-weise Vergleich vergleicht also gar nicht mehr Bytes, die zu den Strings gehören, denn es wurden 3 Byte übersprungen.
Analog werden, wenn, wie in meinem Code, erst der byte-weise Vergleich erfolgt, ohne Anpassung 3 bytes des letzten dwords doppelt verglichen. Die würden dann am Ende fehlen.
-
BTW:
cmovb eax, -1
cmovxx erlaubt keine immediate's - allgemein: CMOVcc r,r/m
-
stimmt, dann eben
sbb eax, eax
-
cld seta eax cmovb eax, -1
- 'seta eax' gibt es ebenfalls nicht: seta al oder ah.
- sbb eax,eax würde für den Fall !CARRY das gesetzte bit in al/ah löschen.
=>mov eax,0 seta al sbb eax,0
-
cmovb schrieb:
cld seta eax cmovb eax, -1
- 'seta eax' gibt es ebenfalls nicht: seta al oder ah.
- sbb eax,eax würde für den Fall !CARRY das gesetzte bit in al/ah löschen.
=>mov eax,0 seta al sbb eax,0
Warum diese Reihenfolge. Hier wird immer -1 rauskommen oder etwa nicht?
Ich brauche jetzt auch noch eine Funktion zum String-Vergleich. Grundsätzlich kann ich da ja genau so verfahren (abgesehen davon, dass ich eine "logisches" big endian byte order habe.
Was mir allerdings Probleme macht ist, wenn die Strings nicht alle Chars auf Upper/Lower Case haben, sondern Groß-/Kleinschreibung enthalten. Wie kann ich das am besten lösen?Ich will keine Reihenfolge dieser Art haben:
ABC
Bertha
Omega
alpha
-
FrEEzE2046 schrieb:
Warum diese Reihenfolge. Hier wird immer -1 rauskommen oder etwa nicht?
Warum? Nochmal drüber nachdenken, wie jede Instruktion die Flags beeinflusst.
Eigentlich hatte ich gehofft, eine Instruktion einsparen zu können - eine Instruktion pro mögliches Ergebnis ist nicht besonders schön. Geht wohl doch nicht. Das mov könnte man noch durch xor ersetzen
push edi push esi lea esi, [eax+ecx-1] lea edi, [edx+ecx-1] std mov edx, ecx shr edx, 2 and ecx, 3 xor eax, eax repe cmpsb jne @@Exit mov ecx, edx lea esi, [esi-3] lea edi, [edi-3] repe cmpsd @@Exit: cld seta al sbb eax, 0 pop esi pop edi
-
mov eax,0 seta al sbb eax,0
1. Zeile: mov eax, 0: Schreibet 0 in eax
2. Zeile: seta (set byte on condition / if above) al: Wenn höher dann 1
3. Zeile: sbb (subtraction with borrow), 0: Wenn Carry-Flag gesetzt um 1 weiter dekrementieren.mmmh...
-
FrEEzE2046 schrieb:
Was mir allerdings Probleme macht ist, wenn die Strings nicht alle Chars auf Upper/Lower Case haben, sondern Groß-/Kleinschreibung enthalten. Wie kann ich das am besten lösen?
Es gibt mehrere Möglichkeiten, z.B. kann erst einmal ein
repe cmps
durchführen, und bei Ungleichheit zusätzlich prüfen, ob die Ungleichheit nur auf Unterschiede zwischen Groß und Klein zurückzuführen ist, und den Vergleich dann ggf. fortsetzen. Auch hier ist die Optimierung per cmpsd denkbar.
-
camper schrieb:
durchführen, und bei Ungleichheit zusätzlich prüfen, ob die Ungleichheit nur auf Unterschiede zwischen Groß und Klein zurückzuführen ist, und den Vergleich dann ggf. fortsetzen. Auch hier ist die Optimierung per cmpsd denkbar.
Danke für deine Antwort.
Was mir aufgefallen ist:
seta al sbb eax, 0
liefert die Ergebnisse genau falsch rum^^. Daher meine bedenken.
-
FrEEzE2046 schrieb:
seta al sbb eax, 0
liefert die Ergebnisse genau falsch rum^^.
esi/edi wurden auch vertauscht.
-
Ich habe dazu noch mal eine generelle Frage:
Wo ist eigentlich der Unterschied zwischen:
lea edi, [eax+ecx-1]
und
mov edi, eax add edi, ecx dec edi
Muss der Prozessor nicht immer die Schritte von "2" durchführen? Im Endeffekt ist "1" doch nur eine kürzere Schreibweise oder bringt dass tatsächlich auch sonst noch was?
EDIT:
Warum sind bei mir die Ergebniswerte vertauscht (1 u. -1)? Ich mach doch nicht viel anders als du:function __MemCompare__LE_DF__(const lho, rho; size: DWORD): Integer; assembler; asm { EAX -> lho result = 0 equal EDX -> rho result = 1 lho < rho ECX -> size result = -1 lho > rho } push edi push esi push ebx std // set direction flag to decrement mov edi, eax mov esi, edx xor eax, eax // null result mov ebx, ecx // save value shr ecx, 2 // look for dword align jz @@Rest mov edx, ebx sub edx, 4 add edi, edx // position on last dword add esi, edx repe cmpsd jne @@Exit @@Rest: mov ecx, ebx and ecx, 3 jz @@Exit add edi, ecx // byte offset add esi, ecx repe cmpsb @@Exit: cld seta al sbb eax, 0 pop ebx pop esi pop edi end; (* of MemCompare *) (* ------------------------------------------------------------------------------------ *)