unit FormDisassembler;

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ComCtrls,
  ExtCtrls, Menus, Disassembler, Base, Debugger;

type
  { TFormDisassembler }

  TFormDisassembler = class(TForm)
    ListView1: TListView;
    MenuItemRemoveBreakPoint: TMenuItem;
    MenuItemAddBreakPoint: TMenuItem;
    MenuItemSetAddress: TMenuItem;
    PopupMenu1: TPopupMenu;
    Timer1: TTimer;
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure ListView1Data(Sender: TObject; Item: TListItem);
    procedure MenuItemAddBreakPointClick(Sender: TObject);
    procedure MenuItemRemoveBreakPointClick(Sender: TObject);
    procedure MenuItemSetAddressClick(Sender: TObject);
    procedure PopupMenu1Popup(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    FReloadPending: Boolean;
    FOnAddBreakPoint: TAddressEvent;
    FOnChangePC: TAddressEvent;
    FLastPC: Word;
    procedure DoChangePC(Address: Word);
    procedure DoAddBreakPoint(Address: Word);
    procedure UpdateDisassemblerPos;
  public
    Debugger: TDebugger;
    Disassembler: TDisassembler;
    procedure Disassemble(Sender: TObject);
    procedure SelectAddress(Address: Word);
    procedure ReloadList;
    procedure Reload;
    function GetCurrentAddress: Word;
    property OnChangePC: TAddressEvent read FOnChangePC write FOnChangePC;
    property OnAddBreakPoint: TAddressEvent read FOnAddBreakPoint write FOnAddBreakPoint;
  end;

var
  FormDisassembler: TFormDisassembler;


implementation

{$R *.lfm}

{ TFormDisassembler }

procedure TFormDisassembler.UpdateDisassemblerPos;
begin
  if FLastPc <> Debugger.Cpu.Pc then begin
    SelectAddress(Debugger.Cpu.PC);
    FLastPc := Debugger.Cpu.Pc;
  end;
end;

procedure TFormDisassembler.FormClose(Sender: TObject;
  var CloseAction: TCloseAction);
begin
end;

procedure TFormDisassembler.FormCreate(Sender: TObject);
begin
  FLastPC := $ffff;
end;

procedure TFormDisassembler.FormDestroy(Sender: TObject);
begin
end;

procedure TFormDisassembler.FormShow(Sender: TObject);
begin
  Reload;
end;

procedure TFormDisassembler.ListView1Data(Sender: TObject; Item: TListItem);
var
  BreakPoint: string;
begin
  if Item.Index < Disassembler.DecodedInstructions.Count then
  with TDecodedInstruction(Disassembler.DecodedInstructions[Item.Index]) do begin
    if Debugger.BreakPoints.Contains(Address) then BreakPoint := ' @'
      else BreakPoint := '';
    Item.Caption := IntToHex(Address, 4) + BreakPoint;
    Item.Data := Disassembler.DecodedInstructions[Item.Index];
    Item.SubItems.Add(GetOpcodesText);
    Item.SubItems.Add(Name);
    Item.SubItems.Add(Parameters);
    Item.SubItems.Add(Comment);
  end;
end;

procedure TFormDisassembler.MenuItemAddBreakPointClick(Sender: TObject);
begin
  if Assigned(ListView1.Selected) then begin
    Debugger.BreakPoints.Add(TDecodedInstruction(ListView1.Selected.Data).Address);
    ReloadList;
  end;
end;

procedure TFormDisassembler.MenuItemRemoveBreakPointClick(Sender: TObject);
begin
  if Assigned(ListView1.Selected) then begin
    Debugger.BreakPoints.Remove(TDecodedInstruction(ListView1.Selected.Data).Address);
    ReloadList;
  end;
end;

procedure TFormDisassembler.MenuItemSetAddressClick(Sender: TObject);
begin
  if Assigned(ListView1.Selected) then begin
    DoChangePC(TDecodedInstruction(ListView1.Selected.Data).Address);
    SelectAddress(TDecodedInstruction(ListView1.Selected.Data).Address);
  end;
end;

procedure TFormDisassembler.PopupMenu1Popup(Sender: TObject);
begin
  MenuItemSetAddress.Enabled := Assigned(ListView1.Selected);
  if Assigned(ListView1.Selected) then begin
    MenuItemRemoveBreakPoint.Enabled := Pos('@', ListView1.Selected.Caption) > 0;
    MenuItemAddBreakPoint.Enabled := not MenuItemRemoveBreakPoint.Enabled;
  end else begin
    MenuItemRemoveBreakPoint.Enabled := False;
    MenuItemAddBreakPoint.Enabled := False;
  end;
end;

procedure TFormDisassembler.Timer1Timer(Sender: TObject);
begin
  UpdateDisassemblerPos;
  if FReloadPending then begin
    FReloadPending := False;
    Disassembler.Disassemble;
    ReloadList;
  end;
end;

procedure TFormDisassembler.Disassemble(Sender: TObject);
begin
  Reload;
end;

procedure TFormDisassembler.DoChangePC(Address: Word);
begin
  if Assigned(FOnChangePC) then FOnChangePC(Address);
end;

procedure TFormDisassembler.DoAddBreakPoint(Address: Word);
begin
  if Assigned(FOnAddBreakPoint) then FOnAddBreakPoint(Address);
end;

procedure TFormDisassembler.SelectAddress(Address: Word);
var
  Item: TListItem;
  Index: Integer;
begin
  Index := Disassembler.DecodedInstructions.SearchAddressIndex(Address);
  if Index >= 0 then begin
    Item := ListView1.Items[Index];
    if Assigned(Item) then begin
      Item.MakeVisible(False);
      //Item.Focused := False;
      //Item.Selected := False;
      //Item.Focused := True;
      //Item.Selected := True;
      ListView1.ItemIndex := Index;
      ListView1.Selected := ListView1.Items[Index];
      ListView1.Items[Index].Focused := True;
    end;
  end;
end;

procedure TFormDisassembler.ReloadList;
begin
  ListView1.Items.Count := Disassembler.DecodedInstructions.Count;
  ListView1.Refresh;
end;

procedure TFormDisassembler.Reload;
begin
  FReloadPending := True;
end;

function TFormDisassembler.GetCurrentAddress: Word;
begin
  if Assigned(ListView1.Selected) then
    Result := TDecodedInstruction(ListView1.Selected.Data).Address
    else Result := 0;
end;

end.

