unit SharpMz800;

interface

uses
  Classes, SysUtils, Z80, MemoryTypes, Base, I8255, I8253;

type
  TMode = (md800, md700);
  TResolution = (rs320x200, rs640x200);

  { TSharpMz800 }

  TSharpMz800 = class
  private
    FOnMemoryMappingChange: TNotifyEvent;
    WriteFormatRegister: Byte; // WF
    ReadFormatRegister: Byte; // RF
    DisplayModeRegister: Byte; // DMD
    MemoryBankControl: array[0..6] of Byte;
    FOnMessage: TMessageEvent;
    Mode: TMode;
    Resolution: TResolution;
    procedure CpuWrite(Address: Word; Data: Byte);
    function CpuRead(Address: Word): Byte;
    function CpuInput(Address: Word): Byte;
    procedure CpuOutput(Address: Word; Data: Byte);
    procedure MesssageExecute(Text: string);
    procedure UpdateMemoryMapping;
    procedure DoMemoryMappingChange;
  public
    Cpu: TCpuZ80;
    Memory: TMemoryData;
    BaseRom: TMemoryData;
    ExtendedRom: TMemoryData;
    VideoRam: TMemoryData;
    CharacterRom: TMemoryData;
    MappedMemory: TMemoryMapped;
    MappedIO: TMemoryIO;
    I8255: T8255;
    I8253: T8253;
    procedure PowerOn;
    procedure PowerOff;
    constructor Create;
    destructor Destroy; override;
    property OnMessage: TMessageEvent read FOnMessage write FOnMessage;
    property OnMemoryMappingChange: TNotifyEvent read FOnMemoryMappingChange
      write FOnMemoryMappingChange;
  end;


implementation

{ TSharpMz800 }

procedure TSharpMz800.CpuWrite(Address: Word; Data: Byte);
begin
  MappedMemory.Write(Address, Data);
end;

function TSharpMz800.CpuRead(Address: Word): Byte;
begin
  Result := MappedMemory.Read(Address);
end;

function TSharpMz800.CpuInput(Address: Word): Byte;
begin
  Result := 0;
  case Address of
    $e0..$e6: Result := MemoryBankControl[Address - $e0];
    $d0..$d3: Result := I8255.Read(Address - $d0);
    $d4..$d7: Result := I8253.Read(Address - $d4);
    else MesssageExecute('Unsupported I/O port read from ' + IntToHex(Address, 4));
  end;
end;

procedure TSharpMz800.CpuOutput(Address: Word; Data: Byte);
begin
  case Address of
    $cc: WriteFormatRegister := Data;
    $cd: ReadFormatRegister := Data;
    $ce: begin
      DisplayModeRegister := Data;
      if (DisplayModeRegister and 8) <> 0 then Mode := md700 else Mode := md800;
      if (DisplayModeRegister and 4) <> 0 then Resolution := rs320x200 else Resolution := rs640x200;
      UpdateMemoryMapping;
    end;
    $e0..$e6: MemoryBankControl[Address - $e0] := Data;
    $d0..$d3: I8255.Write(Address - $d0, Data);
    $d4..$d7: I8253.Write(Address - $d4, Data);
    else MesssageExecute('Unsupported I/O port write to ' + IntToHex(Address, 4) + ' data ' + IntToHex(Data, 2));
  end;
end;

procedure TSharpMz800.MesssageExecute(Text: string);
begin
  if Assigned(FOnMessage) then FOnMessage(Text);
end;

procedure TSharpMz800.UpdateMemoryMapping;
begin
  MappedMemory.Areas.Clear;
  MappedMemory.Areas.AddNew($0, BaseRom);
  case Mode of
    md700: begin
      MappedMemory.Areas.AddNew($d000, VideoRam);
      VideoRam.Size := $1000;
      MappedMemory.Areas.AddNew($e000, MappedIO);
      MappedMemory.Areas.AddNew($e000, ExtendedRom);
    end;
    md800: begin
      MappedMemory.Areas.AddNew($1000, CharacterRom);
      MappedMemory.Areas.AddNew($8000, VideoRam);
      VideoRam.Size := $1000;
      MappedMemory.Areas.AddNew($e000, ExtendedRom);
    end;
  end;
  MappedMemory.Areas.AddNew($0, Memory);
  if Assigned(Cpu) then
    DoMemoryMappingChange;
end;

procedure TSharpMz800.DoMemoryMappingChange;
begin
  if Assigned(OnMemoryMappingChange) then
    FOnMemoryMappingChange(nil);
end;

procedure TSharpMz800.PowerOn;
begin
  Cpu.Running := True;
end;

procedure TSharpMz800.PowerOff;
begin
  Cpu.Running := False;
end;

constructor TSharpMz800.Create;
const
  RomSubDir = 'ROM';
begin
  Mode := md800;
  Resolution := rs320x200;
  I8255 := T8255.Create;
  I8253 := T8253.Create;
  Memory := TMemoryData.Create;
  Memory.Title := 'Main memory';
  Memory.Size := 65536; // $10000
  BaseRom := TMemoryData.Create;
  BaseRom.Title := 'Base ROM';
  BaseRom.LoadFromFile(RomSubDir + DirectorySeparator + 'MZ700A.ROM');
  ExtendedRom := TMemoryData.Create;
  ExtendedRom.Title := 'Extended ROM';
  ExtendedRom.LoadFromFile(RomSubDir + DirectorySeparator + '9Z_504M.ROM');
  CharacterRom := TMemoryData.Create;
  CharacterRom.Title := 'Character ROM (CGROM)';
  CharacterRom.LoadFromFile(RomSubDir + DirectorySeparator + 'CGROM.ROM');
  VideoRam := TMemoryData.Create;
  VideoRam.Title := 'Video memory (VRAM)';
  VideoRam.Size := 4096; // $1000
  MappedMemory := TMemoryMapped.Create;
  MappedMemory.Size := $10000;
  MappedIO := TMemoryIO.Create;
  MappedIO.Title := 'Mapped I/O';
  MappedIO.Size := 8;
  MappedIO.BasePort := $d0;
  MappedIO.OnInput := CpuInput;
  MappedIO.OnOutput := CpuOutput;
  UpdateMemoryMapping;
  Cpu := TCpuZ80.Create;
  Cpu.Memory := MappedMemory;
  Cpu.OnRead := CpuRead;
  Cpu.OnWrite := CpuWrite;
  Cpu.OnInput := CpuInput;
  Cpu.OnOutput := CpuOutput;
  Cpu.OnMessage := MesssageExecute;
end;

destructor TSharpMz800.Destroy;
begin
  FreeAndNil(Cpu);
  FreeAndNil(MappedMemory);
  FreeAndNil(VideoRam);
  FreeAndNil(CharacterRom);
  FreeAndNil(BaseRom);
  FreeAndNil(ExtendedRom);
  FreeAndNil(Memory);
  FreeAndNil(MappedIO);
  FreeAndNil(I8255);
  FreeAndNil(I8253);
  inherited;
end;

end.

