unit InstructionSetGen;

interface

uses
  Classes, SysUtils, Common, Z80InstructionInfo, TypInfo;

type

  { TInstructionSetGen }

  TInstructionSetGen = class
    procedure Generate(FileName: string);
  private
    function GetBlock(var Text: string; StartText, EndText: string): string;
  end;

implementation

{ TInstructionSetGen }

procedure TInstructionSetGen.Generate(FileName: string);
var
  Lines: TStringList;
  Instruction: TStringList;
  Head: TStringList;
  Body: TStringList;
  InitInstructions: TStringList;
  Combined: TStringList;
  Info: TStringList;
  Cell: string;
  Source: string;
  Name: string;
  Opcode: string;
  Bytes: string;
  Cycles: string;
  CyclesFalseCond: string;
  FlagC: string;
  FlagN: string;
  FlagPV: string;
  FlagH: string;
  FlagZ: string;
  FlagS: string;
  Description: string;
  Ident: string;
  Prefix: string;
  NameOnly: string;
  Params: string;
  ParamsPart: string;
  InfoParams: string;
  ParamType: TParamType;
  NewCycles: string;
const
  CellStart = '<td  >';
  CellEnd = '</td>';
  CodeStart = '<code>';
  CodeEnd = '</code>';
  DataStart = '<dd>';
  DataEnd = '</dd>';
  Generated = 'Generated';
begin
  Lines := TStringList.Create;
  Lines.LoadFromFile(FileName);
  Source := Lines.Text;
  Combined := TStringList.Create;
  Instruction := TStringList.Create;
  Instruction.Add('interface');
  Instruction.Add('');
  Instruction.Add('type');
  Instruction.Add('  TInstruction = (');
  Head := TStringList.Create;
  Head.Add('TCpuZ80 = class');
  Body := TStringList.Create;
  Body.Add('implementation');
  Body.Add('');
  InitInstructions := TStringList.Create;
  InitInstructions.Add('procedure TCpuZ80.InitInstructions;');
  InitInstructions.Add('begin');
  Info := TStringList.Create;
  Info.Add('constructor TInstructionSet.Create;');
  Info.Add('begin');
  repeat
    Cell := GetBlock(Source, CellStart, CellEnd);
    if Cell <> '' then begin
      Name := GetBlock(Cell, CodeStart, CodeEnd).Trim;
      if Name = '' then Continue;
      Name := StringReplace(Name, '<var>', '', [rfReplaceAll]);
      Name := StringReplace(Name, '</var>', '', [rfReplaceAll]);
      Params := Name;
      NameOnly := GetStringPart(Params, ' ');

      Ident := StringReplace(Name.ToUpper, ' ', '_', [rfReplaceAll]);
      Ident := StringReplace(Ident, ',', '_', [rfReplaceAll]);
      Ident := StringReplace(Ident, '''', '_Pair', [rfReplaceAll]);
      Ident := StringReplace(Ident, '+', '_Plus_', [rfReplaceAll]);
      Ident := StringReplace(Ident, '(', '', [rfReplaceAll]);
      Ident := StringReplace(Ident, ')', '_Indirect', [rfReplaceAll]);
      Opcode := GetBlock(Cell, DataStart, DataEnd).Trim;
      if Pos('<var>', Opcode) >= 1 then
        Opcode := Copy(Opcode, 1, Pos('<var>', Opcode) - 1).Trim;
      if Pos(' ', Opcode) >= 1 then begin
        Prefix := Copy(Opcode, 1, 2);
        Opcode := Copy(Opcode, 3, MaxInt).Trim;
        if Prefix = 'CB' then Opcode := '1' + Opcode
        else if Prefix = 'DD' then Opcode := '2' + Opcode
        else if Prefix = 'ED' then Opcode := '3' + Opcode
        else if Prefix = 'FD' then Opcode := '4' + Opcode;
      end;
      Bytes := GetBlock(Cell, DataStart, DataEnd).Trim;
      Cycles := GetBlock(Cell, DataStart, DataEnd).Trim;
      if Pos('/', Cycles) >= 1 then begin
        NewCycles := GetStringPart(Cycles, '/');
        CyclesFalseCond := Cycles;
        Cycles := NewCycles;
      end else CyclesFalseCond := '0';

      FlagC := GetBlock(Cell, DataStart, DataEnd).Trim;
      FlagN := GetBlock(Cell, DataStart, DataEnd).Trim;
      FlagPV := GetBlock(Cell, DataStart, DataEnd).Trim;
      FlagH := GetBlock(Cell, DataStart, DataEnd).Trim;
      FlagZ := GetBlock(Cell, DataStart, DataEnd).Trim;
      FlagS := GetBlock(Cell, DataStart, DataEnd).Trim;
      Description := GetBlock(Cell, DataStart, DataEnd).Trim;
      Description := StringReplace(Description, '<var>', '', [rfReplaceAll]);
      Description := StringReplace(Description, '</var>', '', [rfReplaceAll]);

      InfoParams := '';
      Params := Params.ToUpper;
      while Params <> '' do begin
        ParamsPart := GetStringPart(Params, ',');
        ParamType := StrToParamType(ParamsPart, NameOnly);
        if ParamType <> ptNone then
          InfoParams := InfoParams + ', ' + GetEnumName(TypeInfo(TParamType), Integer(ParamType));
      end;
      if Copy(InfoParams, 1, 2) = ', ' then Delete(InfoParams, 1, 2);

      Info.Add('  AddNew(in_' + Ident + ', ''' + NameOnly.ToUpper + ''', [' + InfoParams + '], ''' +
        StringReplace(Description, '''', '''''', [rfReplaceAll]) + ''', ' +
        Cycles + ', ' + CyclesFalseCond + ');');
      Instruction.Add('    in_' + Ident + ' = $' + Opcode + ',');
      InitInstructions.Add('  Instructions[in_' + Ident + '] := ' + Ident + ';');
      Head.Add('    procedure ' + Ident + ';');
      Body.Add('procedure TCpuZ80.' + Ident + ';');
      Body.Add('begin');
      Body.Add('  NotImplemented;');
      Body.Add('end;');
      Body.Add('');
    end else Break;
  until False;
  Instruction.Add('  );');
  Head.Add('end;');
  InitInstructions.Add('end;');
  Info.Add('end;');

  ForceDirectories(Generated);
  Instruction.SaveToFile(Generated + DirectorySeparator + 'Instruction.pas');
  Head.SaveToFile(Generated + DirectorySeparator + 'Head.pas');
  Body.SaveToFile(Generated + DirectorySeparator + 'Body.pas');
  InitInstructions.SaveToFile(Generated + DirectorySeparator + 'InitInstructions.pas');
  Info.SaveToFile(Generated + DirectorySeparator + 'Info.pas');

  Combined.Add(Instruction.Text);
  Combined.Add(Head.Text);
  Combined.Add(Body.Text);
  Combined.Add(InitInstructions.Text);

  Combined.SaveToFile(Generated + DirectorySeparator + 'Combined.pas');

  Combined.Free;
  Instruction.Free;
  Head.Free;
  Body.Free;
  InitInstructions.Free;
  Info.Free;
  Lines.Free;
end;

function TInstructionSetGen.GetBlock(var Text: string; StartText, EndText: string
  ): string;
var
  Index: Integer;
begin
  Result := '';
  Index := Pos(StartText, Text);
  if Index > 0 then begin
    Text := Copy(Text, Index + Length(StartText), MaxInt);
    Index := Pos(EndText, Text);
    if Index > 0 then begin
      Result := Copy(Text, 1, Index - 1);
      Text := Copy(Text, Index + Length(EndText), MaxInt);
    end;
  end;
end;


end.

