Autor Beitrag
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 29.12.06 05:34 
Hallo!

Jetzt hab ich auch mal ein Problem. Und zwar habe ich um ein mathematisches Problem zu lösen ein paar Zeilen Assembler geschrieben.

Daraus rufe ich eine Methode auf, die eigentlich meine Zahl in einen String in Binärdarstellung umwandeln und diesen in ein Memo schreiben soll.
Das Problem ist, dass beim Zugriff auf das Memo eine Schutzverletzung erscheint. Ich habe aber eigentlich das EBX Register wiederhergestellt usw.

Hier der Source, relativ umfangreich kommentiert:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
procedure TfrmMain.btnStartClick(Sender: TObject);

  // Gibt einen 32-Bit Wert zurück, bei dem die angegebenen Bits gesetzt sind.
  function GetMask(FromIndex, Count: Integer): Integer;
  //                  EAX      EDX               EAX
  asm
    push eax            // Register sichern
    mov cl, dl          // Anzahl der Einsen ins Counter-Register
    mov edx, $FFFFFFFF  // EDX mit 1 füllen
    xor eax, eax        // Rückgabewert soll in EAX --> erstmal 0 rein
    shld eax, edx, cl   // Wert in EAX um den Wert im Counter-Register nach
                        // links schieben und mit den Einsen aus EDX füllen
    pop ecx             // Index der ersten Eins in ECX wiederherstellen
    shl eax, cl         // Um den Wert nach links schieben
  end;

  // Rotiert die Bits bei Beachtung der Anzahl der benutzten Bits
  function RotateLeft(Value, RotateBy, Size: Integer): Integer;
  //                   EAX     EDX     ECX               EAX
  asm
    push ebx      // Register sichern
    mov ebx, 1    // 1 in EBX
    shl ebx, cl   // Pos. des nächsthöheren Bits des höchsten Bits steht in CL
    mov ecx, edx  // Schleife initialisieren --> von CL herunterzählen
    @loop:        // Sprungziel für Schleife
    shl eax, 1    // Wert um eins verschieben
    test eax, ebx // Ist das Bit gesetzt?
    jz @nocarry   // Wenn nicht, die 0 im niederwertigsten Bit lassen, ...
    add eax, 1    // ...sonst eine 1 setzen
    xor eax, ebx  // Und die Übertrags-1 weg
    @nocarry:     // Sprungziel für kein Übertrag
    loop @loop    // Schleife ggf. wiederholen
    pop ebx       // Register wiederherstellen
  end;

  // Gibt die Zahl binär mit der angegebenen Anzahl von Stellen aus
  procedure Print(uValue, uDummy, Size: Integer);
  //               EAX     EDX    ECX
  var
    Output: string;
  begin
    Output := StringOfChar('0', Size) ;
    while uValue > 0 do
    begin
      if (uValue and 1) = 1 then
        Output[Size] := '1';
      Dec(Size) ;
      uValue := uValue shr 1;
    end;
    memOutput.Lines.Add(Output); // <-------- Hier kommt der Fehler! <---------------
    Values := Values + #13#10 + AnsiReverseString(Output);
  end;

  // Returns the n for the given parameters
  procedure DoCalculation(a, b, Size: Integer);
  //                     EAX EDX ECX
  var
    Mask: Integer;
  asm
    push edx        // Register sichern
    mov edx, eax    // Die Anzahl ist a
    mov eax, 0      // Startindex ist immer 0

    push ecx
    push edx
    call GetMask    // Die Maske holen, mit deren Hilfe die entsprechenden Bits
                    // gekippt werden --> liegt dann in EAX
    pop edx
    pop ecx

    mov Mask, eax   // In Variable sichern
    mov eax, 0      // Der Startwert für den Wert ist 0
    pop edx         // Um wieviel soll jeweils verschoben werden
    @loopstart:     // Sprungziel für Schleife
    xor eax, Mask   // Bits kippen

    push ecx
    push eax
    call Print      // Ausgeben
    pop eax
    pop ecx

    push ecx
    call RotateLeft // Rotieren, der Wert ist danach wieder in EAX
    pop ecx

    push eax
    push ecx
    call Print      // Ausgeben
    pop ecx
    pop eax

    cmp eax, 0      // Nachsehen, ob EAX schon wieder 0 ist
    jnz @loopstart  // Wenn nicht, weitermachen
  end;

begin
  Values := '';
  DoCalculation(StrToInt(edtA.Text), StrToInt(edtB.Text), StrToInt(edtUmfang.Text));
  memOutput.Text := Trim(Values);
  ShowMessage(IntToStr(memOutput.Lines.Count));
end;


Es wäre schön, wenn mir jemand sagen könnte, warum der Zugriff auf das Memo nicht klappt. Was habe ich falsch gemacht?

Das Projekt hänge ich zum Testen auch komplett an.

// EDIT: Ich habe gerade festgestellt, dass das Projekt auf dem PC meiner Eltern nicht korrekt läuft... Keine Fehlermeldung, es passiert einfach nicht das richtige...

Danke,
Schönen Gruß,
Sebastian
Einloggen, um Attachments anzusehen!
Logikmensch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 390

Win XP
Delphi 2007 Prof., XE2, XE5
BeitragVerfasst: Fr 29.12.06 11:06 
Ich habe mir den Code genauer angeschaut, kann aber auch beim besten Willen keinen Fehler sehen. Auch alle von Delphi vorgeschriebenen Register (hier nur EBX) stellst Du sauber wieder her, und PUSHs zu POPs sind auch okay. Ich würde daher empfehlen, mal mit Hilfe des Debuggers zu schauen, ob beim Erreichen des CALL PRINT und Reinspringen in die Print-Routine der Debugger in der Lage ist, die Lokale Variablen OUTPUT, UVALUE usw. korrekt auszuwerten, wenn er das tut, liegt's an was anderem. Auch beim Debuggen muss innerhalb von PRINT die Auswertung von MEMOUTPUT.LINES.COUNT möglich sein.
Ansonsten musst Du die Routinen nochmal einzeln testen. An dem Aufruf von Assembler nach Delphi muss es nicht unbedingt liegen. Sieht meiner Meinung nach ok aus.

_________________
Es gibt keine Probleme - nur Lösungen!
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: Mo 08.01.07 21:54 
Deine GetMask-Routine könnte IMHO noch etwas optimiert werden ...

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function GetMask(FromIndex, Count: Integer): DWORD;
//  =>  EAX     FromIndex
//  =>  EDX     Cound
//  <=  EAX     Result
asm
    MOV     ECX, EAX    //From index sichern ...
    MOV     EAX, 1      //Let's save an 1 ;-)
    XCHG    EDX, ECX    //We need the shift in CL ...
    SHL     EAX, CL     //Build a power of two ;-)
    DEC     EAX         //Sub one ;-)
    XCHG    EDX, ECX    //Restore our cound *g*
    SHL     EAX, CL     //Make it begin at FromIndex
end;


Sollte im Allgemeinen schneller sein, da kein Stack genutzt wird ... Außerdem werden keine erweiterten Befehle genutzt ... d.h. es läuft auch auf alten Delphi-Versionen ...

Kannst Du mal schauen, welche genaue Fehlermeldung er Dir in besagter Zeile liefert?

Ach ja: Hab deinen Fehler ... Grad noch mal ganz scharf hingeschaut ;-)

Ist ein allgemeines Feature lokaler Prozeduren in einem OOP-Kontext: SDer Self-Zeiger steht nicht zur Verfügung (Stack-Frame); Delphi nimmt aber fälschlicherweise an, dass dies der Fall ist. Kannst Du dadurch lösen, dass Du entweder Self explizit an Print übergibst ODER auf die Formular-Variable.DeinMemo.Lines.Add zugreifst ... Ich würde dir letzteres in deinem Fall empfehlen, da Du ASM und Native mischt und daher der Zugriff auf Self etwas kompliziert ist ...

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

Win XP Prof
Delphi 7 E
BeitragVerfasst: Di 09.01.07 02:13 
Was soll eigentlich dein rotateleft machen frage ich mich die ganze Zeit. Das funktioniert aber so wie gewollt???
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 09.01.07 03:20 
:autsch: :autsch: Auf die Idee, die Einsen so zu erzeugen hätte ich auch selber kommen können...
So geht das GetMask natürlich besser...

Und was den Fehler angeht: Du hast Recht, ich hab daraus frmMain.memOutput.Lines.Add gemacht und dann ging es.

Ich bin aus dem Assembler-Code nicht so ganz schlau geworden, deshalb hab ich nicht gesehen, was Delphi eigentlich macht. Also ich meine, wie es versucht auf das Memo zuzugreifen. Und leider ist mir nicht ganz klar, wie Delphi das intern mit Self macht. Naja, mal googeln...

Jedenfalls vielen, vielen Dank, jetzt geht es wie es soll... :zustimm:

user profile iconAllesquarks hat folgendes geschrieben:
Was soll eigentlich dein rotateleft machen frage ich mich die ganze Zeit. Das funktioniert aber so wie gewollt???

Um das auch nicht unbeantwortet zu lassen:
Ich möchte nur z.B. 12 Bits betrachten. Nun rotiere ich um 2 Bits nach links. Dann muss jeweils nach dem Rotieren um Eins danach das 15. Bit auf das erste gepackt werden. Die Rotate-Befehle von Assembler können aber nur ganze DWord / Word / etc. Werte rotieren...
Beispiel:
Ich betrachte nur die niederwertigsten 12 Bit, hier also die rechten:
00001010 00000000
Die vier Nullen Links sollen also ignoriert werden, jetzt rotiere ich um 2:
00001000 00000010
Allesquarks
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: Di 09.01.07 04:43 
Achso hab den dritten Parameter einfach nicht verstanden. Aber dafür brauchst du doch gar keine Schleife, aber wens funzt dann kann ich auch verstehen wenn de das nicht mehr ändern willst:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
function rotateleft(value,size,steps:integer):integer;
asm 
   push esi;
   push ebx;

   rol eax,cl;//rotiert eax um steps, in eax werden im weiteren Bits separiert für die das zulässig war in ebx die Überlaufbits   
   mov ebx,eax;

   xchg ecx,edx;//rol etc gehen nur mit cl deshalb tauschen

   ror ebx,cl;//rotieren um size und zwar rechts herum dies führt dazu dass nun die Überlaufbits in ebx ganz rechts stehen da ja vorher schon um steps nach links gedreht wurde 

   mov esi,$FFFFFFFF;//Maske anfangen
   shl esi,cl;
   not esi;//jetzt sind in einer maske soviele Bits wie des values gesetzt

   and eax,esi;//hier werden aus eax zunächst alles links weggeschnitten   

   xchg ecx,edx;//wieder anderer Zähler nach ecx

   mov esi,$FFFFFFFF;//neue Maske
   shl esi,cl;//soviele Bits von rechts nicht gesetzt wie rotiert werden sollte
   
   and eax,esi;//hier werden die Bits aus eax (der ersten Näherung) geschnitten, die überlaufen und hier also fehlerhaft sind

   not esi;//soviele Bits von rechts gesetzt wie rotiert werden sollte Bsp: 5 steps => ...000011111

   and ebx,esi;//in ebx wird hierduch nur der Überlauf selektiert

   or eax,ebx;//fertig zusammenfügen von rotate und Überlauf

   pop ebx;
   pop esi;
end;
jaenicke Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19326
Erhaltene Danke: 1749

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 09.01.07 05:44 
Ja, dass das so ungefähr gehen müsste, ist mir gerade in dem Moment durch den Kopf geschossen, als ich geschrieben habe, was die Funktion macht ;-). Ich habs aber dann doch nicht versucht. (Hatte gerade mit einem anderen Thread hier zu tun, wo ich ne Grafik zur Erklärung eines Speicherlecks gemacht habe...)

Deine Funktion funktioniert jedenfalls zwar einzeln, aber leider nicht eingebaut im Programm. Naja, ich werd aber den Fehler bestimmt noch finden. Aber den suche ich später...

Danke jedenfalls! :D

user profile iconAllesquarks hat folgendes geschrieben:
aber wens funzt dann kann ich auch verstehen wenn de das nicht mehr ändern willst:

Es geht hier weniger darum, dass es geht... Dann hätte ich es auch in Delphi schreiben können. ;-)

Nein, ich will mich mal wieder intensiver mit Assembler beschäftigen, denn das ganze was ich vor 5 Jahren im Rahmen meines kleinen "Betriebssystems" mal gelernt hatte hab ich schon wieder vergessen. (Es gibt ungefragt eine Liste aller Dateien im Hauptverzeichnis aus und sagt dann Hatschi. Wenn man dann Gesundheit eingibt, dagt es Danke ;-), und das wars --> Resetknopf :lol:.)
Aber für die Semesterferien im März plane ich eine Neuauflage, diesmal vielleicht mit so sinnvollen Befehlen wie Beep :lol:. Auch will ich mal nicht nur das Hauptverzeichnis der Diskette auslesen, wie das letzte Mal, sondern vielleicht auch Unterordner. Naja, mal sehen...