Autor Beitrag
fidionael
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 232

Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
BeitragVerfasst: 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:

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 232

Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
BeitragVerfasst: Fr 02.06.06 14:07 
Vielen Dank, dass mit dem Label läuft schonmal :)

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Fr 02.06.06 14:17 
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 232

Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
BeitragVerfasst: Fr 02.06.06 14:19 
Vielen Dank! Nun sind alle meine Fragen vorerst beantwortet :)

Die fertige Funktion sieht nun folgendermaßen aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
function Calc(x: Integer) : Integer;
asm
  mov [esp-4],0       //Setzen des Zwischenspeichers auf 0
  mov ecx,x           //Setzen der Laufvariablen auf x
  @sumup:             //@sumup-Label
    add [esp-4],ecx   //Zwischenspeicher += Laufvariable
    dec ecx           //Verringern der Laufvariablen
    jnz @sumup        //Wenn Laufvariable größer 0, springe zu @sumup
  mov eax,[esp-4]     //Rückgabewert der Funktion setzen
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Fr 02.06.06 14:23 
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 232

Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 417
Erhaltene Danke: 2

XP
FPC mit Lazarus
BeitragVerfasst: Fr 02.06.06 15:20 
mahlzeit!
wenn man den loop befehl nimmt, kann man ein klein wenig arbeit sparen:
ausblenden 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    // ecx wird bei loop automatisch decrementiert, 
     // dadurch keine dec ecx & cmp ecx,0 nötig
  mov temp, eax        
end;
result := temp;
end;


okay, ist vllt nicht kürzer, aber auch ganz nett :rofl:
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 232

Win XP SP2, Ubuntu 6.06
Delphi 7 PE, Delphi 3 Prof
BeitragVerfasst: 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:
user profile iconrizla 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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.
ausblenden 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

ausblenden 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 ...



user profile iconAllesquarks 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.

user profile iconAllesquarks 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.



user profile iconuall@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 ...

user profile iconuall@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.



user profile iconrizla hat folgendes geschrieben:
mahlzeit!
wenn man den loop befehl nimmt, kann man ein klein wenig arbeit sparen:
ausblenden 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    // ecx wird bei loop automatisch decrementiert, 
     // dadurch keine dec ecx & cmp ecx,0 nötig
  mov temp, eax        
end;
result := temp;
end;


okay, ist vllt nicht kürzer, aber auch ganz nett :rofl:
btw: nettes tutorial haste da gemacht, hoffe am we zeit zu haben, es zu studieren!
:rizla:


Geht auch effizienter ...
ausblenden 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 ;-)



user profile iconfidionael 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.