Einzelnen Beitrag anzeigen

Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#1

[asm/delphi] AV bei Ausführung von MOV-Instruction

  Alt 23. Jan 2010, 22:34
Hallo ihr ,

angeregt durch die verschiedenen BF-Interpreter im Brainfuck-Thread, wollte ich versuchen, einen kleinen JIT-Compiler zu schreiben.

Bisher habe ich nur die Rechenoperationen (und Schleifen) implementiert, und soweit scheint der generierte Binärcode - wenn auch nicht optimal - zumindest korrekt zu sein, denn wenn ihn mir in der CPU-Ansicht von Delphi in disassemblierter Form ansehe, kommt das Erwartete heraus.

Leider kann ich den Code dennoch nicht ausführen - denn schon bei der ersten Instruktion (MOV) erscheint schon eine Zugriffsverletzung:
Code:
---------------------------
Bfcompiler
---------------------------
Zugriffsverletzung bei Adresse 015f043c. Schreiben von Adresse 015f043c.
---------------------------
OK  
---------------------------
$015f043c ist die Adresse an der der reservierte Speicher für das Brainfuck-Programm anfängt.

Der relevante Code ist folgender:
Delphi-Quellcode:
  TBrainFuckCompiler = class(TBrainfuckParser)
  protected
    // In diesen Stream wird der Programmcode geschrieben
    FMemStream: TMemoryStream;
    procedure DoCompile; virtual;
    { ... }
  public
    procedure Compile;
    { ... }
  end;

  TBrainFuckRunner = class(TBrainfuckCompiler)
  protected
    // In diesem Array ist der Speicher für das Programm reserviert:
    FMemory: packed array[0..$10000] of Byte;
    procedure DoCompile; override;
  public
    procedure Run;
  end;

{ TBrainFuckRunner }

procedure TBrainFuckRunner.DoCompile;
var
  B: Byte;
  DW: Longint;
begin
  // Speicher-Adresse in ECX übergeben
  B := $B8 + 1; // MOV to ECX
  FMemStream.Write(B, 1);

  // Speicheradresse schreiben
  DW := LongInt(@(FMemory[0]));
  FMemStream.Write(DW, 4);

  // Programmcode kompilieren
  inherited;

  // RETurn (for execution)
  B := $C3; // RET
  FMemStream.Write(B, 1);
end;

procedure TBrainFuckRunner.Run;
var
  address: pointer;
begin
  address := FMemStream.Memory;
  asm
    call address;
  end;
end;
Die eigentliche Kompilierung findet in TBrainfuckCompiler.DoCompile statt - dabei habe ich es so gelöst, dass aktuelle "Zelle" des Brainfuckprogramms (Pointer auf die Speicheradresse) immer im Register ECX gehalten wird. Wenn dann eine Zelle weitergerückt wird, wird einfach ECX inkrementiert usw.
Natürlich muss ECX dafür zuerst mit der richtigen Adresse initialisiert werden, und das geschieht vor der eigentlichen Kompilierung in TBrainFuckRunner.DoCompile.

Die generierten Instruktionen sehen dann (laut Delphis Disassembler) für das Brainfuck-Programm "++>-<-->+>+++" z.B. so aus:
Code:
// $015f043c = Basis-Speicheradresse für das Brainfuck-Programm
01600460 B93C045F01       mov ecx,$015f043c // Und hier kratzt er dann auch schon ab...

01600465 8B01             mov eax,[ecx]
01600467 40               inc eax
01600468 8BC8             mov ecx,eax
0160046A 8B01             mov eax,[ecx]
0160046C 40               inc eax
0160046D 8BC8             mov ecx,eax
0160046F 41               inc ecx
01600470 8B01             mov eax,[ecx]
01600472 48               dec eax
01600473 8BC8             mov ecx,eax
01600475 49               dec ecx
01600476 8B01             mov eax,[ecx]
01600478 48               dec eax
01600479 8BC8             mov ecx,eax
0160047B 8B01             mov eax,[ecx]
0160047D 48               dec eax
0160047E 8BC8             mov ecx,eax
01600480 41               inc ecx
01600481 8B01             mov eax,[ecx]
01600483 40               inc eax
01600484 8BC8             mov ecx,eax
01600486 41               inc ecx
01600487 8B01             mov eax,[ecx]
01600489 40               inc eax
0160048A 8BC8             mov ecx,eax
0160048C 8B01             mov eax,[ecx]
0160048E 40               inc eax
0160048F 8BC8             mov ecx,eax
01600491 8B01             mov eax,[ecx]
01600493 40               inc eax
01600494 8BC8             mov ecx,eax
01600496 C3               ret
(Nicht perfekt, ich weiß - aber es ist auch mein erstes Projekt dieser Art und viel Erfahrung mit Assembler habe ich auch nicht )

Wie schon beschrieben, kratzt der bei der ersten Instruktion mit oben gezeigter Fehlermeldung ab. Was mich vor allem verwundert, ist, dass er sich wegen eines Schreibzugriffs beschwert - ich schreibe doch gar nicht an die Adresse, oder? Eigentlich soll nur dieser Wert ins Register ECX geschoben werden.

Kann mich jemand mit mehr Assembler-Erfahrung vielleicht erleuchten?
Vielen Dank schon mal!
Angehängte Dateien
Dateityp: zip brainfuck_189.zip (229,0 KB, 2x aufgerufen)
  Mit Zitat antworten Zitat