unit Core;

interface

uses
  Classes, SysUtils, ActnList, Controls, FormMain, Forms, ExtCtrls, FormMemory,
  SharpMz800, FormDisassembler, FormCpu, FormScreen, FormMessages, FormCallStack,
  Debugger, Disassembler;

type

  { TCore }

  TCore = class(TDataModule)
    AViewCallStack: TAction;
    AInstructionSetGen: TAction;
    AViewMessages: TAction;
    AGoToAddress: TAction;
    AReset: TAction;
    AViewScreen: TAction;
    AViewCpu: TAction;
    AStop: TAction;
    AStepOut: TAction;
    ARun: TAction;
    AStepIn: TAction;
    AStepOver: TAction;
    ARunToCursor: TAction;
    APause: TAction;
    AViewDissssembler: TAction;
    AViewMemory: TAction;
    AExit: TAction;
    ActionList1: TActionList;
    ImageList1: TImageList;
    TimerUpdate: TTimer;
    procedure AExitExecute(Sender: TObject);
    procedure AGoToAddressExecute(Sender: TObject);
    procedure AInstructionSetGenExecute(Sender: TObject);
    procedure APauseExecute(Sender: TObject);
    procedure AResetExecute(Sender: TObject);
    procedure ARunExecute(Sender: TObject);
    procedure ARunToCursorExecute(Sender: TObject);
    procedure AStepInExecute(Sender: TObject);
    procedure AStepOutExecute(Sender: TObject);
    procedure AStepOverExecute(Sender: TObject);
    procedure AStopExecute(Sender: TObject);
    procedure AViewCallStackExecute(Sender: TObject);
    procedure AViewCpuExecute(Sender: TObject);
    procedure AViewDissssemblerExecute(Sender: TObject);
    procedure AViewMemoryExecute(Sender: TObject);
    procedure AViewMessagesExecute(Sender: TObject);
    procedure AViewScreenExecute(Sender: TObject);
    procedure DataModuleCreate(Sender: TObject);
    procedure DataModuleDestroy(Sender: TObject);
    procedure TimerUpdateTimer(Sender: TObject);
  private
    FUpdateInterfacePending: Boolean;
    procedure DoChangePC(Address: Word);
    procedure DebuggerChange(Sender: TObject);
  public
    FormMain: TFormMain;
    FormMemory: TFormMemory;
    FormDisassembler: TFormDisassembler;
    FormCpu: TFormCpu;
    FormScreen: TFormScreen;
    FormMessages: TFormMessages;
    FormCallStack: TFormCallStack;
    SharpMz800: TSharpMz800;
    Debugger: TDebugger;
    Disassembler: TDisassembler;
    procedure UpdateInterface;
  end;

var
  Core: TCore;


implementation

{$R *.lfm}

uses
  Z80, FormGoToAddress, InstructionSetGen;

{ TCore }

procedure TCore.AExitExecute(Sender: TObject);
begin
  Application.Terminate;
end;

procedure TCore.AGoToAddressExecute(Sender: TObject);
var
  FormGoToAddress: TFormGoToAddress;
  Address: LongInt;
begin
  FormGoToAddress := TFormGoToAddress.Create(nil);
  try
    if FormGoToAddress.ShowModal = mrOk then begin
      if TryStrToInt(FormGoToAddress.EditAddress.Text, Address) then
        FormDisassembler.SelectAddress(Address);
    end;
  finally
    FormGoToAddress.Free;
  end;
end;

procedure TCore.AInstructionSetGenExecute(Sender: TObject);
var
  InstructionSetGen: TInstructionSetGen;
begin
  InstructionSetGen := TInstructionSetGen.Create;
  InstructionSetGen.Generate('Z80/InstructionSet.html');
  InstructionSetGen.Free;
end;

procedure TCore.APauseExecute(Sender: TObject);
begin
  SharpMz800.Cpu.Paused := True;
  UpdateInterface;
end;

procedure TCore.AResetExecute(Sender: TObject);
var
  IsRunning: Boolean;
  IsPaused: Boolean;
begin
  IsRunning := SharpMz800.Cpu.Running;
  IsPaused := SharpMz800.Cpu.Paused;
  SharpMz800.Cpu.Running := False;
  SharpMz800.Cpu.Reset;
  Debugger.Reset;
  if Assigned(FormMessages) then FormMessages.Messages.Clear;
  SharpMz800.Cpu.Running := IsRunning;
  SharpMz800.Cpu.Paused := IsPaused;
  UpdateInterface;
end;

procedure TCore.ARunExecute(Sender: TObject);
begin
  SharpMz800.Cpu.Running := True;
  UpdateInterface;
end;

procedure TCore.ARunToCursorExecute(Sender: TObject);
begin
  Debugger.DebugMode := dmStopAddress;
  Debugger.DebugStopAddress := FormDisassembler.GetCurrentAddress;
  Debugger.Cpu.Running := True;
  UpdateInterface;
end;

procedure TCore.AStepInExecute(Sender: TObject);
begin
  Debugger.DebugMode := dmStepIn;
  Debugger.Cpu.Running := True;
  UpdateInterface;
end;

procedure TCore.AStepOutExecute(Sender: TObject);
begin
  Debugger.DebugMode := dmStepOut;
  Debugger.Cpu.Running := True;
  UpdateInterface;
end;

procedure TCore.AStepOverExecute(Sender: TObject);
begin
  Debugger.DebugMode := dmStepOver;
  Debugger.Cpu.Running := True;
  UpdateInterface;
end;

procedure TCore.AStopExecute(Sender: TObject);
begin
  SharpMz800.Cpu.Running := False;
  UpdateInterface;
end;

procedure TCore.AViewCallStackExecute(Sender: TObject);
begin
  if not Assigned(FormCallStack) then begin
    FormCallStack := TFormCallStack.Create(nil);
    FormCallStack.Debugger := Debugger;
  end;
  FormCallStack.Show;
end;

procedure TCore.AViewCpuExecute(Sender: TObject);
begin
  if not Assigned(FormCpu) then begin
    FormCpu := TFormCpu.Create(nil);
    FormCpu.Cpu := SharpMz800.Cpu;
  end;
  FormCpu.Show;
end;

procedure TCore.AViewDissssemblerExecute(Sender: TObject);
begin
  if not Assigned(FormDisassembler) then begin
    FormDisassembler := TFormDisassembler.Create(nil);
    FormDisassembler.OnChangePC := DoChangePC;
    FormDisassembler.Debugger := Debugger;
    FormDisassembler.Disassembler := Disassembler;
    SharpMz800.OnMemoryMappingChange := FormDisassembler.Disassemble;
  end;
  FormDisassembler.Show;
  FormMessages.AddMessage(FormDisassembler.Disassembler.InstructionSet.Check(SharpMz800.Cpu.Instructions));
end;

procedure TCore.AViewMemoryExecute(Sender: TObject);
begin
  if not Assigned(FormMemory) then begin
    FormMemory := TFormMemory.Create(nil);
    FormMemory.Areas.Add(SharpMz800.Memory);
    FormMemory.Areas.Add(SharpMz800.BaseRom);
    FormMemory.Areas.Add(SharpMz800.ExtendedRom);
    FormMemory.Areas.Add(SharpMz800.VideoRam);
    FormMemory.Areas.Add(SharpMz800.CharacterRom);
    FormMemory.Areas.Add(SharpMz800.MappedIO);
  end;
  FormMemory.Show;
end;

procedure TCore.AViewMessagesExecute(Sender: TObject);
begin
  if not Assigned(FormMessages) then begin
    FormMessages := TFormMessages.Create(nil);
    SharpMz800.OnMessage := FormMessages.AddMessage;
  end;
  FormMessages.Show;
end;

procedure TCore.AViewScreenExecute(Sender: TObject);
begin
  if not Assigned(FormScreen) then begin
    FormScreen := TFormScreen.Create(nil);
  end;
  FormScreen.Show;
end;

procedure TCore.DataModuleCreate(Sender: TObject);
begin
  SharpMz800 := TSharpMz800.Create;
  Disassembler := TDisassembler.Create;
  Disassembler.Memory := SharpMz800.MappedMemory;
  Debugger := TDebugger.Create;
  Debugger.Cpu := SharpMz800.Cpu;
  Debugger.OnChange := DebuggerChange;
  Debugger.Disassembler := Disassembler;
  UpdateInterface;
  FormMain := TFormMain.Create(nil);
  FormMain.Show;
end;

procedure TCore.DataModuleDestroy(Sender: TObject);
begin
  SharpMz800.Cpu.Running := False;
  if Assigned(FormCpu) then FreeAndNil(FormCpu);
  if Assigned(FormDisassembler) then FreeAndNil(FormDisassembler);
  if Assigned(FormMemory) then FreeAndNil(FormMemory);
  if Assigned(FormScreen) then FreeAndNil(FormScreen);
  if Assigned(FormMessages) then FreeAndNil(FormMessages);
  if Assigned(FormCallStack) then FreeAndNil(FormCallStack);
  FreeAndNil(FormMain);
  FreeAndNil(Debugger);
  FreeAndNil(Disassembler);
  FreeAndNil(SharpMz800);
end;

procedure TCore.TimerUpdateTimer(Sender: TObject);
begin
  if FUpdateInterfacePending then begin
    FUpdateInterfacePending := False;
    UpdateInterface;
  end;
end;

procedure TCore.DoChangePC(Address: Word);
begin
  SharpMz800.Cpu.PC := Address;
end;

procedure TCore.DebuggerChange(Sender: TObject);
begin
  if Assigned(FormCallStack) then FormCallStack.Reload;
  if Assigned(FormCpu) then FormCpu.Reload;
  FUpdateInterfacePending := True;
end;

procedure TCore.UpdateInterface;
begin
  ARun.Enabled := not SharpMz800.Cpu.Running or (SharpMz800.Cpu.Running and SharpMz800.Cpu.Paused);
  AStop.Enabled := SharpMz800.Cpu.Running and not SharpMz800.Cpu.Paused;
  APause.Enabled := SharpMz800.Cpu.Running and not SharpMz800.Cpu.Paused;
  AStepIn.Enabled := not SharpMz800.Cpu.Running or (SharpMz800.Cpu.Running and SharpMz800.Cpu.Paused);
  AStepOut.Enabled := not SharpMz800.Cpu.Running or (SharpMz800.Cpu.Running and SharpMz800.Cpu.Paused);
  AStepOver.Enabled := not SharpMz800.Cpu.Running or (SharpMz800.Cpu.Running and SharpMz800.Cpu.Paused);
  ARunToCursor.Enabled := not SharpMz800.Cpu.Running or (SharpMz800.Cpu.Running and SharpMz800.Cpu.Paused);
end;

end.

