Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Assembler in Delphi! Speichersack? (https://www.delphipraxis.net/9620-assembler-delphi-speichersack.html)

neolithos 30. Sep 2003 11:17


Assembler in Delphi! Speichersack?
 
Ich habe zur Übung ein Stück Quell-Code in Assembler umgewandelt.

Delphi-Quellcode:
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;
Leider gibt es an einer anderen stelle im Programm ein Speicherproblem.
Wo mach ich was, was ich nicht darf?

sakura 30. Sep 2003 11:23

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:
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;
...:cat:...

neolithos 30. Sep 2003 11:31

Re: Assembler in Delphi! Speichersack?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Nach Änderung entsteht folgender Fehler...

negaH 30. Sep 2003 11:41

Re: Assembler in Delphi! Speichersack?
 
Du vergisst den Stack korrekt aufzuräumen.

Delphi-Quellcode:
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;
Zudem verstehe ich nicht warum du
Delphi-Quellcode:
  Result := (apt.x div 17) + (apt.y div 17) * 8
in Assembler als
Delphi-Quellcode:
Result := (apt.x div 17) or (apt.y div 17) * 8
codiert hast.

Zudem JNS = Jump if not sign, Springe wenn EAX < 0 ! ist falsch

Delphi-Quellcode:
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;
Gruß Hagen

negaH 30. Sep 2003 11:44

Re: Assembler in Delphi! Speichersack?
 
Delphi-Quellcode:
  MOV @Result,-1
Ist schon korrekt gewesen wenn ein lokale Stackframe eingerichtet wurde. Der Assembler macht daraus

Delphi-Quellcode:
  MOV EAX,-1
Gruß Hagen

negaH 30. Sep 2003 11:49

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

neolithos 30. Sep 2003 12:47

Re: Assembler in Delphi! Speichersack?
 
Erstmal danke für die neuen Erkenntnisse!

Delphi-Quellcode:
   JA   @@Nowhere
Delphi-Quellcode:
LEA  EAX,[EDI + EAX * 8] //  Result = P.X div 17 + P.Y div 17 * 8
Doch diese zwi Zeilen sind mir noch unklar? :gruebel:

neolithos 30. Sep 2003 13:07

Re: Assembler in Delphi! Speichersack?
 
So ich habe gerade versucht das gegenstück zu verbessern!

Delphi-Quellcode:
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;
Geht das noch eleganter?
Warum kann man ecx nicht nutzen

neolithos 30. Sep 2003 13:22

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.

negaH 30. Sep 2003 14:28

Re: Assembler in Delphi! Speichersack?
 
Delphi-Quellcode:
// 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
}
Gruß Hagen

neolithos 30. Sep 2003 21:00

Re: Assembler in Delphi! Speichersack?
 
mit dem LEA
das leuchtet mir noch nicht ein!
Warum verwendest du das in dem zusammenhang?

negaH 30. Sep 2003 21:33

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

neolithos 30. Sep 2003 21:34

Re: Assembler in Delphi! Speichersack?
 
die zwei lea's mahnt Delphi mit einem Compiler fehler!

negaH 30. Sep 2003 21:43

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:

   IMUL EAX,17
   IMUL EDX,17
Alle anderen Kombinationen sind dann in fact langsammer.
Man könnte:

Delphi-Quellcode:
   MOV ESI,EAX
   SHL EAX,4
   ADD EAX,ESI

   MOV ESI,EDX
   SHL EDX,4
   ADD EDX,ESI
benutzen, aber das dürfte wohl langsammer sein.

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.

neolithos 30. Sep 2003 21:50

Re: Assembler in Delphi! Speichersack?
 
Es war bis jetzt nur ein Syntaxfehler dabei! Respekt!

Trotzdem weis ich noch nix mit dem lea anzufangen?

negaH 30. Sep 2003 22:01

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:
type
  PDaten = ^TDaten;
  TDaten = packed record
    X,Y: Integer;
  end;

  PDatenArray = ^TDatenArray;
  TDatenArray = array[0..127] of TDaten;
Wir habe nun einen Zeiger P der vom Typ PDatenArray ist und wollen ausgehend von P auf das neunte Element, sprich @P[9] zugreifen.

Delphi-Quellcode:
   
   MOV EAX,P
   MOV ECX,9
   
   LEA EAX,[EAX + ECX * 8]
ECX = 9 also Index ins Array in P und SizeOf(TDaten) = 8 also PChar(P) + SizeOf(TDaten) * 9 = @P[9]
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

neolithos 30. Sep 2003 22:02

Re: Assembler in Delphi! Speichersack?
 
Der Vollständigkeithalber...

Delphi-Quellcode:
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;
lea hab ich jetzt gefunden!

negaH 30. Sep 2003 22:11

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:

    PUSH ECX
    MOV ECX,8
    MOV EAX,EDX
    XOR EDX,EDX
    DIV ECX
    POP ECX
Oder noch einfacher

Delphi-Quellcode:
const
  Dimension: Cardinal = 8;
asm
....
   
   MOV EAX,EDX
   XOR EDX,EDX
   DIV Dimension
 
...
end;
Gruß Hagen

neolithos 30. Sep 2003 22:25

Re: Assembler in Delphi! Speichersack?
 
Darf ich mal loswerten!

80x86-Assembler komplizierter als 80c535!

Anmerk:
ebx getauscht!

negaH 30. Sep 2003 22:56

Re: Assembler in Delphi! Speichersack?
 
Zitat:

80x86-Assembler komplizierter als 80c535
Naja, das hängt davon ab. Ich habe in meinem Leben mindestens 7 verschiedene Assembler und CPU Typen programmiert. Das was am meisten nervt sind RISC Prozessoren, und Assembler deren Parameterstrukturen vertauscht sind, sprich statt MOV EAX,Value, eben MOV Value,EAX aber beide meinen das Gleiche. Ansonsten ist es mir egal ob es ein x86, ein Motorola oder PIC oder SmartCard Chip ist.

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 ?

Daniel B 30. Sep 2003 23:01

Re: Assembler in Delphi! Speichersack?
 
Zitat:

Zitat von negaH
Das was am meisten nervt sind RISC Prozessoren

Oooch, jetz sei doch nicht so ;)
Zitat:

Ansonsten ist es mir egal ob es ein PIC ist.
Gut zu wissen. :mrgreen: Endlich mal einer den ich damit nerven kann ;)


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