Autor |
Beitrag |
fidionael
      
Beiträge: 232
Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
|
Verfasst: Fr 02.06.06 13:49
Hallo,
ich eigne mir grad ein wenig Assembler an, nur funktioniert das alles trotz zahlreicher Tutorials noch nicht recht so, wie ich mir das vorgestellt hatte.
Ich wollte in Delphi ein kleines Programm schreiben, dass eine Zahl aufsummiert (wenn die Zahl z. B. 5 ist, dann soll das Ergebnis 1+2+3+4+5 = 15 sein). Nun habe ich eine kleine Assembler-Unterfunktion in meiner ButtonClick-Prozedur eingefügt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function Calc(x: Word) : Word; asm xor ax,ax mov cx,x sumup: add ax,cx dec cx jnz sumup end; |
Ich bekomme allerdings beim Kompilieren die Fehlermeldung, dass sumup unbekannt ist. Wie stelle ich das richtig an? Vermutlich handelt es sich hierbei um eine sehr dumme Frage, doch ich komme wirklich nicht weiter und wäre sehr dankbar, wenn jemand kurz Zeit für mich opfern könnte
Mfg
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Fr 02.06.06 13:55
Das ist unbekannt, da es ein Labnel ist. Grundsetzlich muss man in inline assembler labels nicht deklarieren wie in pascal aber schon kenntlich machen im Gegensatz zu Variablen. Das macht man wie folgt:
@@mylabel:
jnz @@mylabel;
Desweiteren schau dir mal Aufrufkonventionen an, es konnte sein, dass du deine übergebene Zahl x:word gleich durch xor ax,ax löscht, da diese whrscheinlich in eax übergeben wird.
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 02.06.06 14:00
Ja, es wird dierekt X gelöscht. Du (Delphi) benutzt die Register Convention, von daher ist X in EAX gespeichert.
Desweiteren solltest du die 32 Bit Instructions nehmen. Also XOR EAX, EAX anstatt XOR AX, AX.
Es compiliert wegen dem SumUp nicht. Davor muss ein @ oder @@.
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
fidionael 
      
Beiträge: 232
Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
|
Verfasst: Fr 02.06.06 14:07
Vielen Dank, dass mit dem Label läuft schonmal
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| function Calc(x: Integer) : Integer; asm mov ecx,x @sumup: add eax,ecx dec ecx jnz @sumup end; |
Nun ist es ja, wie ihr auch schon bemerkt habt, so, dass ich ax, oder eax nicht einfach 0 zuweisen kann und dann ausummieren. Kann ich denn in Assembler irgendwie eine Variable deklarieren, mit welcher ich das tun kann?
Danke schonmal!
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 02.06.06 14:12
Nein kannst du nicht, sonst wärs kein Assembler. Du kannst Register benutzen und den Stack (der so gut wie die Variablen ist)
z.b. Kannst du
MOV [ESP-4], EAX
machen um etwas zu speichern, wobei es wichtig ist, dass du alles in ESP-X machst, wobei X mod 4 = 0 (nicht zwingend aber übersichtlicher)
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Fr 02.06.06 14:17
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function Calc(x: Integer) : Integer; asm mov ecx,x xor eax,eax; @sumup: add eax,ecx dec ecx jnz @sumup end; |
Das Problem ist doch nur, dass du eax löscht bevor du es in das "Zählregister" gebracht hast, wenn du das zuerst tust, dann fertig.
|
|
fidionael 
      
Beiträge: 232
Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
|
Verfasst: Fr 02.06.06 14:19
Vielen Dank! Nun sind alle meine Fragen vorerst beantwortet
Die fertige Funktion sieht nun folgendermaßen aus:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| function Calc(x: Integer) : Integer; asm mov [esp-4],0 mov ecx,x @sumup: add [esp-4],ecx dec ecx jnz @sumup mov eax,[esp-4] end; |
Nochmals danke für eure Hilfe!
//Edit: Oh ja stimmt, das Verschieben der mov ecx,x - Zeile nach oben hätte das Problem auch gelöst ... Naja, jetzt weiß ich für die Zukunft auch schon wo ich zwischenspeichern kann, wenn es nötig sein sollte 
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 02.06.06 14:23
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| function Calc(x: Integer) : Integer; asm MOV ECX, EAX JMP @@in
@@sumup: ADD EAX, ECX
@@in: DEC ECX JNZ @@sumup end;
function Calc2(x: Integer): Integer; asm MOV EDX, EAX IMUL EAX, EDX ADD EAX, EDX SHR EAX, 1 end; |
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
uall@ogc
      
Beiträge: 1826
Erhaltene Danke: 11
Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
|
Verfasst: Fr 02.06.06 14:33
Du kennst ja die Register die du benutzen kannst?
EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP
Wobei EBP und ESP am Ende der funktion wieder stimmen sollten.
_________________ wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit
|
|
fidionael 
      
Beiträge: 232
Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
|
Verfasst: Fr 02.06.06 14:40
Naja ich bin wie gesagt noch in der Lern-Phase
Jedenfalls danke für die schnelle und kompetente Hilfe.
|
|
rizla
      
Beiträge: 417
Erhaltene Danke: 2
XP
FPC mit Lazarus
|
Verfasst: Fr 02.06.06 15:20
mahlzeit!
wenn man den loop befehl nimmt, kann man ein klein wenig arbeit sparen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| function Calc(x: Integer) : Integer; var temp: integer; begin asm mov ecx,x xor eax,eax @sumup: add eax, ecx loop @sumup mov temp, eax end; result := temp; end; |
okay, ist vllt nicht kürzer, aber auch ganz nett
btw: nettes tutorial haste da gemacht, hoffe am we zeit zu haben, es zu studieren!
:rizla:
_________________ if you have what they want - they'll find a way to take it (bruce sterling)
WOW - 10 JAHRE Mitglied beim Delphi-Forum. Wie die Zeit vergeht, Freunde.
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Fr 02.06.06 15:32
Ich frage mich auch immer, warum der Befehl loop kaum verwendet wird. Tatsache ist jedoch, dass er in reichlich wenig assembler codes, die ich gesehen habe vorkommt.
So weit ich weiß, werden beim decrementieren, zumindest von ecx, die Flages gesetzt. Folglich kann man das cmp ecx,0 weglassen und direkt jnz benutzen.
Dennoch schätze ich dass die loop Variante ein bischen schneller ist (aber nur Aufgrund der Befehlsdecodierung), wenn man bedenkt, dass intel bei seiner "next generation Microarchitecture" als große Neuerung die Einführung der zusammenfassenden Befehle cmp mit anschließendem Sprungbefehl feiert.
|
|
fidionael 
      
Beiträge: 232
Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
|
Verfasst: Fr 02.06.06 15:57
Ich benutze loop aus folgendem Grund nicht: Ich lerne Assembler (unter anderem) weil es viel schneller als die Hochsprachen sind und in einem Tutorial, welches ich studiert habe folgendes steht:
Zitat: |
Die Verwendung von LOOP soll sich bis zum 80286 noch lohnen, in neueren Prozessoren ist die Kombination DEC...JXX schneller.
|
Und wenn ich mich schon durch diese in meinen Augen sehr schwere Materie kämpfe, dann will ich wenigstens maximale Effizienz
Mfg
PS: rizla hat folgendes geschrieben: | btw: nettes tutorial haste da gemacht, hoffe am we zeit zu haben, es zu studieren! |
Meinst du mein Tut über Spieleprogrammierung oder welches?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Fr 02.06.06 17:13
Für das Problem mit dem unbekannten Label gibt es zwei Möglichkeiten:
1. Verwendung lokaler Label - diese fangen immer mit @@ im Namen an.
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| function Calc(x: Word) : Word; asm xor ax, ax mov cx, x @@sumup: add ax, cx dec cx jnz @sumup end; |
2. Verwendung deklarierter Label - diese müssen innerhalb der Funktion bekannt gemacht werden
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| function Calc(x: Word) : Word; label sumup; asm xor ax, ax mov cx, x sumup: add ax, cx dec cx jnz sumup end; |
In deinem Source ist aber noch ein anderer Fehler drin:
Durch die Aufrufkonvention Register (Standard bei Pascal), werden die Parameter in den Registern EAX, ECX und EDX übergeben (bzw. bei Dir AX, DX und CX. Die Rückgabe erfolgt immer im Register EAX\AX\ST0 (ST0 für Floats)
Der Parameter X wird somit im AX-Register übergeben --> Wird in deinem Source durch die erste Zeile sofort wieder überschrieben.
Du müsstest also die ersten beiden Source-Zeilen tauschen.
//Edit:
Achja, weil hier von einigen sehr viel Dünnes gepostet wurde noch ein paar Anmerkungen hinterher ...
Allesquarks hat folgendes geschrieben: | Das ist unbekannt, da es ein Labnel ist. |
Falsch: Das ist unbekannt, da dieses Label sich nicht auf den ASM-Block lokal, sondern auf die Routine bezieht und daher deklariert werden müsste. Siehe oben.
Allesquarks hat folgendes geschrieben: | Grundsetzlich muss man in inline assembler labels nicht deklarieren wie in pascal aber schon kenntlich machen im Gegensatz zu Variablen. |
Nein. In Inline-Assembler muss man nur Assembler-lokale Labels (die mit @@ am Anfang) nicht deklarieren. Hat man in einer Funktion 2 ASM-Blöke, die Gegenseitig auf Labels zugreifen, müssen diese Labels (als Labels der Funktion) deklariert werden.
uall@ogc hat folgendes geschrieben: | Du kennst ja die Register die du benutzen kannst?
EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP |
Einspruch!!! Von ESP und EBP sollte man, wenn man nicht wirklich weiß, was man mach IMMER die Finger lassen ...
uall@ogc hat folgendes geschrieben: | Wobei EBP und ESP am Ende der funktion wieder stimmen sollten. |
Unter Delphi sollten sogar EBX, ESI, EDI auch nocht stimmen.
Für die Freie Veränderung stehen also nur EAX, EDX und ECX zur Verfügung. EBX, ESI und EDI sollte man, wenn man sie veränder vorher sichern und nachher wiederherstellen, ESP und EBP sollte man sogar ganz in Ruhe lassen.
@uall: Ich weiß, dass Du in der Hinsicht deine Sources anders aufbaust, aber für Anfänger ist diese Richtlinie so wohl besser um Fehler zu vermeiden.
rizla hat folgendes geschrieben: | mahlzeit!
wenn man den loop befehl nimmt, kann man ein klein wenig arbeit sparen:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| function Calc(x: Integer) : Integer; var temp: integer; begin asm mov ecx,x xor eax,eax @sumup: add eax, ecx loop @sumup mov temp, eax end; result := temp; end; |
okay, ist vllt nicht kürzer, aber auch ganz nett
btw: nettes tutorial haste da gemacht, hoffe am we zeit zu haben, es zu studieren!
:rizla: |
Geht auch effizienter ...
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| function Calc(x: Word) : DWORD; asm movzx ecx, x and eax, $0000FFFF jmp @@start @@sumup: add eax, ecx @@start: loop @@sumup end; |
Wobei uall's Funktion, die das direkt rechnet, immer noch am effizientesten ist
fidionael hat folgendes geschrieben: | Ich benutze loop aus folgendem Grund nicht: Ich lerne Assembler (unter anderem) weil es viel schneller als die Hochsprachen sind und in einem Tutorial, welches ich studiert habe folgendes steht:
Zitat: |
Die Verwendung von LOOP soll sich bis zum 80286 noch lohnen, in neueren Prozessoren ist die Kombination DEC...JXX schneller.
|
Und wenn ich mich schon durch diese in meinen Augen sehr schwere Materie kämpfe, dann will ich wenigstens maximale Effizienz  |
Dann wundert's mich natürlich, dass Du nicht die direkte Formel dafür verwendest *g*
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
|