![]() |
Assembler in Delphi! Speichersack?
Ich habe zur Übung ein Stück Quell-Code in Assembler umgewandelt.
Delphi-Quellcode:
Leider gibt es an einer anderen stelle im Programm ein Speicherproblem.
function TColorPalette.HitColorText(apt: TPoint): Integer;
var x : Integer; {begin if PtInRect(ClientRect, apt) then if ((apt.x mod 17) > 14) or ((apt.y mod 17) > 14) then Result := -1 else Result := (apt.x div 17) + (apt.y div 17) * 8 else Result := -1; end;} asm // apt.x div 17 mov eax, apt.x mov ecx, 17 push edx mov edx, 0 div ecx cmp edx, 14 jns @nowhere mov x, eax // apt.y div 17 pop edx mov eax, apt.y mov edx, 0 div ecx cmp edx, 14 jns @nowhere // Result := x + y(eax) * 8 shl eax, 3 or eax, x cmp eax, 40 jns @nowhere mov @Result, eax jmp @1 @nowhere: mov @Result, -1 // Result := -1 @1: end; Wo mach ich was, was ich nicht darf? |
Re: Assembler in Delphi! Speichersack?
Das Ergebnis wird für Integer-Funktionen in EAX zurückgegeben, ansonsten kannst Du natürlich auch Result ansprechen, allerdings ohne die @ Operatoren ;-)
Code:
...:cat:...
function TColorPalette.HitColorText(apt: TPoint): Integer;
var x : Integer; {begin if PtInRect(ClientRect, apt) then if ((apt.x mod 17) > 14) or ((apt.y mod 17) > 14) then Result := -1 else Result := (apt.x div 17) + (apt.y div 17) * 8 else Result := -1; end;} asm // apt.x div 17 mov eax, apt.x mov ecx, 17 push edx mov edx, 0 div ecx cmp edx, 14 jns @nowhere mov x, eax // apt.y div 17 pop edx mov eax, apt.y mov edx, 0 div ecx cmp edx, 14 jns @nowhere // Result := x + y(eax) * 8 shl eax, 3 or eax, x cmp eax, 40 jns @nowhere [color=#ff0000][s] mov @Result, eax[/s][/color] jmp @1 @nowhere: mov [color=#ff001b]EAX[/color], -1 // Result := -1 @1: end; |
Re: Assembler in Delphi! Speichersack?
Liste der Anhänge anzeigen (Anzahl: 1)
Nach Änderung entsteht folgender Fehler...
|
Re: Assembler in Delphi! Speichersack?
Du vergisst den Stack korrekt aufzuräumen.
Delphi-Quellcode:
Zudem verstehe ich nicht warum du
asm
// apt.x div 17 mov eax, apt.x mov ecx, 17 push edx <--- hier EDX auf Stack mov edx, 0 div ecx cmp edx, 14 jns @nowhere <---- hier EXIT ohne EDX vom Stack zu holen mov x, eax // apt.y div 17 pop edx <---- hier EDX vom Stack mov eax, apt.y mov edx, 0 div ecx cmp edx, 14 jns @nowhere // Result := x + y(eax) * 8 shl eax, 3 or eax, x cmp eax, 40 jns @nowhere mov @Result, eax jmp @1 @nowhere: mov @Result, -1 // Result := -1 <----- wenn obiges JNS True ist wird EDX nicht vom Stack geholt @1: end;
Delphi-Quellcode:
in Assembler als
Result := (apt.x div 17) + (apt.y div 17) * 8
Delphi-Quellcode:
codiert hast.
Result := (apt.x div 17) or (apt.y div 17) * 8
Zudem JNS = Jump if not sign, Springe wenn EAX < 0 ! ist falsch
Delphi-Quellcode:
Gruß Hagen
function TColorPalette.HitColorText(apt: TPoint): Integer;
{ var x : Integer; begin if PtInRect(ClientRect, apt) then if ((apt.x mod 17) > 14) or ((apt.y mod 17) > 14) then Result := -1 else Result := (apt.x div 17) + (apt.y div 17) * 8 else Result := -1; end;} asm // in EAX = Self, EDX = @TPoint PUSH EDI MOV ECX,17 MOV EAX,[EDX].TPoint.X MOV EDI,[EDX].TPoint.Y TEST EAX,EAX JS @@Nowhere // P.X < 0 ?? TEST EDI,EDI JS @@Nowhere // P.Y < 0 ?? XOR EDX,EDX DIV ECX CMP EDX,14 JA @@Nowhere // P.X mod 17 > 14 ?? XCHG EAX,EDI // EDI = P.X div 17 XOR EDX,EDX DIV ECX CMP EDX,14 JA @@Nowhere LEA EAX,[EDI + EAX * 8] // Result = P.X div 17 + P.Y div 17 * 8 JMP @@Exit @@Nowhere: XOR EAX,EAX DEC EAX @@Exit POP EDI end; |
Re: Assembler in Delphi! Speichersack?
Delphi-Quellcode:
Ist schon korrekt gewesen wenn ein lokale Stackframe eingerichtet wurde. Der Assembler macht daraus
MOV @Result,-1
Delphi-Quellcode:
Gruß Hagen
MOV EAX,-1
|
Re: Assembler in Delphi! Speichersack?
Übrigens, diese Funktion ist ein ideales Beispiel warum man noch Assembler benutzen sollte.
Denn der Delphi Compiler/Optimierer kann nicht die 4 nötigen Divisionen als 2 Divisionen codieren. Er erkennt deren Abhängigkeiten nicht. Da die Division als solches eine der langsamsten Operationen ist, ist hier ein handgemachter Assembler von Vorteil. Man kann zwar die Division durch 17 beschleunigen, allerdings nützt das hier nicht viel da wir noch den Modulare Rest durch 17 benötigen. Dafür wiederum gibt es keine schnellere Variante als DIV, vorrausgesetzt wir benötigen den modularen Rest UND den Quotienten zu 17. Gruß Hagen |
Re: Assembler in Delphi! Speichersack?
Erstmal danke für die neuen Erkenntnisse!
Delphi-Quellcode:
JA @@Nowhere
Delphi-Quellcode:
Doch diese zwi Zeilen sind mir noch unklar? :gruebel:
LEA EAX,[EDI + EAX * 8] // Result = P.X div 17 + P.Y div 17 * 8
|
Re: Assembler in Delphi! Speichersack?
So ich habe gerade versucht das gegenstück zu verbessern!
Delphi-Quellcode:
Geht das noch eleganter?
function TColorPalette.GetColorRect(aiCol: Integer): TRect;
var x, y : Integer; {begin if aiCol = -1 then Result := Rect(0, 0, 0, 0) else begin x := aiCol mod 8; y := aiCol div 8; Result := Rect(x * 17, y * 17, x * 17 + 14, y * 17 + 14); end; end; } asm push edi test edx, edx js @@nullrect // aiCol < 0 mov edi, 8 mov eax, edx xor edx, edx div edi // aiCol div 8 mov x, edx // = x mov y, eax // = y mov edi, 17 // Abstand // berachne mov eax, x mul edi mov @Result.Left, eax mov eax, y mul edi mov @Result.Top, eax mov eax, x mul edi add eax, 14 mov @Result.Right, eax mov eax, y mul edi add eax, 14 mov @Result.Bottom, eax jmp @@exit @@nullrect: mov @Result.Left, 0 mov @Result.Top, 0 mov @Result.Right, 0 mov @Result.Bottom, 0 @@exit: pop edi end; Warum kann man ecx nicht nutzen |
Re: Assembler in Delphi! Speichersack?
Die division möchte ich nicht dur shr 3 oder and 7 ersetzen da es ja mal 9 oder n spalten werden könnten.
|
Re: Assembler in Delphi! Speichersack?
Delphi-Quellcode:
Gruß Hagen
// EAX = Self, EDX = aCol, ECX = @Result.TRect
asm TEST EDX,EDX JNS @@1 XOR EAX,EAX MOV [ECX].TRect.Left,EAX MOV [ECX].TRect.Right,EAX MOV [ECX].TRect.Top,EAX MOV [ECX].TRect.Bottom,EAX RET @@1: MOV EAX,EDX SHR EDX,3 // aCol div 8 AND EAX,7 // aCol mod 8 LEA EAX,[EAX * 16 + EAX] // X * 16 + X = X * 17 LEA EDX,[EDX * 16 + EDX] // Y * 16 + Y = Y * 17 MOV [ECX].TRect.Left,EAX MOV [ECX].TRect.Top,EDX ADD EAX,14 ADD EDX,14 MOV [ECX].TRect.Right,EAX MOV [ECX].TRect.Borrom,EDX end; { JA = Jump if Above, springe wenn größer, Vorzeichen wird ignoriert JS = Jump if Sign, springe wenn < 0 JNS = Jump not if Sign, springe nicht wenn < 0, bzw. springe wenn >= 0 LEA = Load Effective Address } |
Re: Assembler in Delphi! Speichersack?
mit dem LEA
das leuchtet mir noch nicht ein! Warum verwendest du das in dem zusammenhang? |
Re: Assembler in Delphi! Speichersack?
Weil es effizient ist :)
LEA EAX,[EAX * 8 + EAX] ist identisch mit IMUL EAX,9 aber es ist wesentlich schneller und kompakter. Zusätzlich kann man mit LEA Instruktionen ein besseres Instruktions-shuffling/pairing durchführen. Dies bedeutet LEA OpCodes können auf Pipelined Prozessoren, also allen neueren Prozessoren, mit den nachfolgenden/vorhergehenden Operationen in parallel durchgeführt werden. Dadurch erhöht sich die Performance des Algos. um 2,3,4,x mal, je nach Anzahl der Pipelines. IMUL/MUL können nur in der V Pipeline decodiert und ausgeführt werden. Somit sind sie langsammer und "blockieren" die CPU. Aber um das alles zu begreifen und zudem noch die ganzen Unterschiede zwischen den einzelnen CPU's zu berücksichtigen, musste dich schon selber bemühen und viele Dokumentationen lesen. Um gleichmal allen anderen Experten zuvorzukommen: obige Ausschweifungen sind stark vereinfacht und auf's wesentlichste reduziert. Sie sind also eine fast schon falsche Verallgemeinerung der Tatsachen. Wollte man alles in diesem Zusammenhang warum man nun LEA statt IMUL/MUL benutzt erklären könnte man ein Buch draus machen :) Gruß hagen |
Re: Assembler in Delphi! Speichersack?
die zwei lea's mahnt Delphi mit einem Compiler fehler!
|
Re: Assembler in Delphi! Speichersack?
Shit, das hatte ich schon erwartet, war mir aber nicht mehr sicher :)
Ok, das Problem ist man darf nur mit 1,2,4,8 multiplizieren !. Also ersetzen durch:
Delphi-Quellcode:
Alle anderen Kombinationen sind dann in fact langsammer.IMUL EAX,17 IMUL EDX,17 Man könnte:
Delphi-Quellcode:
benutzen, aber das dürfte wohl langsammer sein.
MOV ESI,EAX
SHL EAX,4 ADD EAX,ESI MOV ESI,EDX SHL EDX,4 ADD EDX,ESI Sorry für meinen Fehler :) Gruß Hagen PS: alle obigen Postings habe ich NICHT getestet, sie sind aus dem Kopf und es dürfte deine Aufgabe sein sie korrekt lauffähig zu machen. Dies ist übrigens eben das Problem mit Assembler, jeder noch so kleine Source muß ausgiebig getestet werden. |
Re: Assembler in Delphi! Speichersack?
Es war bis jetzt nur ein Syntaxfehler dabei! Respekt!
Trotzdem weis ich noch nix mit dem lea anzufangen? |
Re: Assembler in Delphi! Speichersack?
Tja wie erklärt man was was einem selber wie Wasser und Luft der Programmierung vorkommt, schwierig ?
Also, LEA = Load Effective Address = Lade Effektive Adresse ist eigentlich ein OpCode der zur schnelleren Adressberechnungen mit Zeigern entwickelt wurde. Also in EAX ist ein Zeiger auf eine Datenstruktur in einem Array. Diese Datenstruktur wurde so entwickelt das sie aus zwei Integern besteht. Z.b:
Delphi-Quellcode:
Wir habe nun einen Zeiger P der vom Typ PDatenArray ist und wollen ausgehend von P auf das neunte Element, sprich @P[9] zugreifen.
type
PDaten = ^TDaten; TDaten = packed record X,Y: Integer; end; PDatenArray = ^TDatenArray; TDatenArray = array[0..127] of TDaten;
Delphi-Quellcode:
ECX = 9 also Index ins Array in P und SizeOf(TDaten) = 8 also PChar(P) + SizeOf(TDaten) * 9 = @P[9]MOV EAX,P MOV ECX,9 LEA EAX,[EAX + ECX * 8] oder eben LEA EAX,[P + 9 * 8] -> LEA EAX,[EAX + ECX * 8] wenn in ECX z.b. ein Zähler ist. Somit ist LEA ein OpCode der besonders in Recordbasierten Strukturen die über Indexe arbeiten benutzt wird. Aber wer sagt das EAX ein Zeiger auf ein Array sein muß, und das ECX ein Index in dieses Array sein muß und das 8 die Größe eines Records sein muß ?? Keiner, man kann also LEA benutzen um 1 MOV + 1 MUL mit 2,4,8 + 1 ADD/SUB in eine einzigste Instruktion zu packen. Gruß Hagen |
Re: Assembler in Delphi! Speichersack?
Der Vollständigkeithalber...
Delphi-Quellcode:
lea hab ich jetzt gefunden!
asm
test edx, edx jns @@1 xor eax, eax mov [ecx].TRect.Left, eax mov [ecx].TRect.Top, eax mov [ecx].TRect.Right, eax mov [ecx].TRect.Bottom, eax ret @@1: mov eax, edx xor edx, edx push ebx mov ebx, 8 // damit die 8 durch etwas anderes ersetzt werden kann div ebx // eax = y edx = x pop ebx imul eax, 17 imul edx, 17 mov [ecx].TRect.Left, edx mov [ecx].TRect.Top, eax add eax, 14 add edx, 14 mov [ecx].TRect.Right, edx mov [ecx].TRect.Bottom, eax end; |
Re: Assembler in Delphi! Speichersack?
Vermeide EBX, ok das hat jetzt keine wesentliche Bedeutung in deinem Beispiel aber es ist besser EBX nur im Notfall zu benutzen, um besseren Assembler zu erzeugen der auch in Kylix ohne Probleme oder Nacharbeiten lauffähig ist. In deinem Fall nehme ECX
Delphi-Quellcode:
Oder noch einfacher PUSH ECX MOV ECX,8 MOV EAX,EDX XOR EDX,EDX DIV ECX POP ECX
Delphi-Quellcode:
Gruß Hagen
const
Dimension: Cardinal = 8; asm .... MOV EAX,EDX XOR EDX,EDX DIV Dimension ... end; |
Re: Assembler in Delphi! Speichersack?
Darf ich mal loswerten!
80x86-Assembler komplizierter als 80c535! Anmerk: ebx getauscht! |
Re: Assembler in Delphi! Speichersack?
Zitat:
Wo du aber recht hast ist der Punkt das manch "Deutsches" Produkt eine bessere und klarere Logik enthält. Gruß Hagen PS: Was hast du mit einem 80c535 gemacht ? |
Re: Assembler in Delphi! Speichersack?
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:44 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz