unit MemoryTypes;

interface

uses
  Classes, SysUtils, Generics.Collections, Base;

type

  { TMemory }

  TMemory = class
  private
    function GetSize: Integer; virtual;
    procedure SetSize(AValue: Integer); virtual; abstract;
  public
    Title: string;
    Position: Integer;
    function Read(Address: Word): Byte; virtual; abstract;
    procedure Write(Address: Word; Data: Byte); virtual; abstract;
    function ReadByte: Byte;
    function ReadWord: Word;
    procedure LoadFromFile(FileName: string); virtual; abstract;
    destructor Destroy; override;
    property Size: Integer read GetSize write SetSize;
  end;

  { TMemoryData }

  TMemoryData = class(TMemory)
    DataPtr: PByte;
    function GetSize: Integer; override;
    procedure SetSize(AValue: Integer); override;
    function Read(Address: Word): Byte; override;
    procedure Write(Address: Word; Data: Byte); override;
    procedure LoadFromFile(FileName: string); override;
  end;

  { TMemoryIO }

  TMemoryIO = class(TMemory)
  private
    FOnInput: TReadEvent;
    FOnOutput: TWriteEvent;
    FSize: Integer;
  public
    BasePort: Byte;
    function GetSize: Integer; override;
    procedure SetSize(AValue: Integer); override;
    function Read(Address: Word): Byte; override;
    procedure Write(Address: Word; Data: Byte); override;
    property OnInput: TReadEvent read FOnInput write FOnInput;
    property OnOutput: TWriteEvent read FOnOutput write FOnOutput;
  end;

  { TMemoryMappedArea }

  TMemoryMappedArea = class
    Base: Word;
    Memory: TMemory;
    function InRange(Address: Word): Boolean;
  end;

  { TMemoryMappedAreas }

  TMemoryMappedAreas = class(TObjectList<TMemoryMappedArea>)
    function AddNew(Base: Word; Memory: TMemory): TMemoryMappedArea;
  end;

 { TMemoryMapped }

  TMemoryMapped = class(TMemory)
  private
    FSize: Integer;
  public
    Areas: TMemoryMappedAreas;
    function GetSize: Integer; override;
    procedure SetSize(AValue: Integer); override;
    function Read(Address: Word): Byte; override;
    procedure Write(Address: Word; Data: Byte); override;
    constructor Create;
    destructor Destroy; override;
  end;


implementation

{ TMemoryMappedAreas }

function TMemoryMappedAreas.AddNew(Base: Word; Memory: TMemory): TMemoryMappedArea;
begin
  Result := TMemoryMappedArea.Create;
  Result.Base := Base;
  Result.Memory := Memory;
  Add(Result);
end;

{ TMemoryMappedArea }

function TMemoryMappedArea.InRange(Address: Word): Boolean;
begin
  Result := (Address >= Base) and (Address < (Base + Memory.Size));
end;

{ TMemoryMapped }

function TMemoryMapped.GetSize: Integer;
begin
  Result := FSize;
end;

procedure TMemoryMapped.SetSize(AValue: Integer);
begin
  FSize := AValue;
end;

function TMemoryMapped.Read(Address: Word): Byte;
var
  I: Integer;
begin
  for I := 0 to Areas.Count - 1 do
    if Areas[I].InRange(Address) then begin
      Result := Areas[I].Memory.Read(Address - Areas[I].Base);
      Break;
    end;
end;

procedure TMemoryMapped.Write(Address: Word; Data: Byte);
var
  I: Integer;
begin
  for I := 0 to Areas.Count - 1 do
    if Areas[I].InRange(Address) then begin
      Areas[I].Memory.Write(Address - Areas[I].Base, Data);
      Break;
    end;
end;

constructor TMemoryMapped.Create;
begin
  inherited;
  Areas := TMemoryMappedAreas.Create;
end;

destructor TMemoryMapped.Destroy;
begin
  FreeAndNil(Areas);
  inherited;
end;

{ TMemoryData }

function TMemoryData.GetSize: Integer;
begin
  Result := MemSize(DataPtr);
end;

procedure TMemoryData.SetSize(AValue: Integer);
begin
  ReAllocMem(DataPtr, AValue);
end;

function TMemoryData.Read(Address: Word): Byte;
begin
  Result := PByte(DataPtr + Address)^;
end;

procedure TMemoryData.Write(Address: Word; Data: Byte);
begin
  PByte(DataPtr + Address)^ := Data;
end;

procedure TMemoryData.LoadFromFile(FileName: string);
var
  FileStream: TFileStream;
begin
  FileStream := TFileStream.Create(FileName, fmOpenRead);
  try
    Size := FileStream.Size;
    FileStream.Read(DataPtr^, FileStream.Size);
  finally
    FreeAndNil(FileStream);
  end;
end;

{ TMemoryIO }

function TMemoryIO.GetSize: Integer;
begin
  Result := FSize;
end;

procedure TMemoryIO.SetSize(AValue: Integer);
begin
  FSize := AVAlue;
end;

function TMemoryIO.Read(Address: Word): Byte;
begin
  if Assigned(FOnInput) then FOnInput(BasePort + Address);
end;

procedure TMemoryIO.Write(Address: Word; Data: Byte);
begin
  if Assigned(FOnOutput) then FOnOutput(BasePort + Address, Data);
end;

{ TMemory }

function TMemory.GetSize: Integer;
begin
  Result := 0;
end;

function TMemory.ReadByte: Byte;
begin
  if Position < Size then Result := Read(Position)
    else Result := 0;
  Position := Position + SizeOf(Byte);
end;

function TMemory.ReadWord: Word;
begin
  Result := Read(Position) or (Read(Position + 1) shl 8);
  Position := Position + SizeOf(Word);
end;

destructor TMemory.Destroy;
begin
  Size := 0;
  inherited;
end;

end.

