| 1 | unit Disassembler;
 | 
|---|
| 2 | 
 | 
|---|
| 3 | interface
 | 
|---|
| 4 | 
 | 
|---|
| 5 | uses
 | 
|---|
| 6 |   Classes, SysUtils, CpuZ80, Instructions, StrUtils, Memory, Generics.Collections;
 | 
|---|
| 7 | 
 | 
|---|
| 8 | type
 | 
|---|
| 9 | 
 | 
|---|
| 10 |   { TDecodedInstruction }
 | 
|---|
| 11 | 
 | 
|---|
| 12 |   TDecodedInstruction = class
 | 
|---|
| 13 |     Address: Word;
 | 
|---|
| 14 |     Opcodes: array of Byte;
 | 
|---|
| 15 |     Name: string;
 | 
|---|
| 16 |     Parameters: string;
 | 
|---|
| 17 |     constructor Create;
 | 
|---|
| 18 |     procedure AddOpcode(Data: Byte);
 | 
|---|
| 19 |     function GetOpcodesText: string;
 | 
|---|
| 20 |   end;
 | 
|---|
| 21 | 
 | 
|---|
| 22 |   { TDecodedInstructions }
 | 
|---|
| 23 | 
 | 
|---|
| 24 |   TDecodedInstructions = class(TObjectList<TDecodedInstruction>)
 | 
|---|
| 25 |     function SearchAddress(Address: Word): TDecodedInstruction;
 | 
|---|
| 26 |   end;
 | 
|---|
| 27 | 
 | 
|---|
| 28 |   { TDisassembler }
 | 
|---|
| 29 | 
 | 
|---|
| 30 |   TDisassembler = class
 | 
|---|
| 31 |     InstructionSet: TInstructionSet;
 | 
|---|
| 32 |     Memory: TMemory;
 | 
|---|
| 33 |     DecodedInstructions: TDecodedInstructions;
 | 
|---|
| 34 |     procedure Disassemble;
 | 
|---|
| 35 |     procedure SaveToFile(FileName: string);
 | 
|---|
| 36 |     procedure ToLines(Lines: TStrings);
 | 
|---|
| 37 |     constructor Create;
 | 
|---|
| 38 |     destructor Destroy; override;
 | 
|---|
| 39 |   end;
 | 
|---|
| 40 | 
 | 
|---|
| 41 | 
 | 
|---|
| 42 | implementation
 | 
|---|
| 43 | 
 | 
|---|
| 44 | { TDecodedInstructions }
 | 
|---|
| 45 | 
 | 
|---|
| 46 | function TDecodedInstructions.SearchAddress(Address: Word): TDecodedInstruction;
 | 
|---|
| 47 | var
 | 
|---|
| 48 |   I: Integer;
 | 
|---|
| 49 | begin
 | 
|---|
| 50 |   I := 0;
 | 
|---|
| 51 |   while (I < Count) and (Items[I].Address <> Address) do Inc(I);
 | 
|---|
| 52 |   if I < Count then Result := Items[I]
 | 
|---|
| 53 |     else Result := nil;
 | 
|---|
| 54 | end;
 | 
|---|
| 55 | 
 | 
|---|
| 56 | { TDecodedInstruction }
 | 
|---|
| 57 | 
 | 
|---|
| 58 | constructor TDecodedInstruction.Create;
 | 
|---|
| 59 | begin
 | 
|---|
| 60 |   Parameters := '';
 | 
|---|
| 61 | end;
 | 
|---|
| 62 | 
 | 
|---|
| 63 | procedure TDecodedInstruction.AddOpcode(Data: Byte);
 | 
|---|
| 64 | begin
 | 
|---|
| 65 |   SetLength(Opcodes, Length(Opcodes) + 1);
 | 
|---|
| 66 |   Opcodes[Length(Opcodes) - 1] := Data;
 | 
|---|
| 67 | end;
 | 
|---|
| 68 | 
 | 
|---|
| 69 | function TDecodedInstruction.GetOpcodesText: string;
 | 
|---|
| 70 | var
 | 
|---|
| 71 |   I: Integer;
 | 
|---|
| 72 | begin
 | 
|---|
| 73 |   Result := '';
 | 
|---|
| 74 |   for I := 0 to Length(Opcodes) - 1 do
 | 
|---|
| 75 |     Result := Result + IntToHex(Opcodes[I], 2) + ' ';
 | 
|---|
| 76 |   Result := Trim(Result);
 | 
|---|
| 77 | end;
 | 
|---|
| 78 | 
 | 
|---|
| 79 | procedure TDisassembler.Disassemble;
 | 
|---|
| 80 | var
 | 
|---|
| 81 |   J: Integer;
 | 
|---|
| 82 |   Value: Integer;
 | 
|---|
| 83 |   Instruction: TInstruction;
 | 
|---|
| 84 |   InstructionInfo: TInstructionInfo;
 | 
|---|
| 85 |   DecodedInstruction: TDecodedInstruction;
 | 
|---|
| 86 | begin
 | 
|---|
| 87 |   Memory.Position := 0;
 | 
|---|
| 88 |   while Memory.Position < Memory.Size do begin
 | 
|---|
| 89 |     DecodedInstruction := TDecodedInstruction.Create;
 | 
|---|
| 90 |     DecodedInstruction.Address := Memory.Position;
 | 
|---|
| 91 |     Value := Memory.ReadByte;
 | 
|---|
| 92 |     DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 93 |     if Value = $cb then begin
 | 
|---|
| 94 |       Value := Memory.ReadByte;
 | 
|---|
| 95 |       DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 96 |       Value := $cb00 or Value;
 | 
|---|
| 97 |     end else
 | 
|---|
| 98 |     if Value = $dd then begin
 | 
|---|
| 99 |       Value := Memory.ReadByte;
 | 
|---|
| 100 |       DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 101 |       Value := $dd00 or Value;
 | 
|---|
| 102 |     end else
 | 
|---|
| 103 |     if Value = $ed then begin
 | 
|---|
| 104 |       Value := Memory.ReadByte;
 | 
|---|
| 105 |       DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 106 |       Value := $ed00 or Value;
 | 
|---|
| 107 |     end else
 | 
|---|
| 108 |     if Value = $fd then begin
 | 
|---|
| 109 |       Value := Memory.ReadByte;
 | 
|---|
| 110 |       DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 111 |       Value := $fd00 or Value;
 | 
|---|
| 112 |     end;
 | 
|---|
| 113 |     if (Value >= 0) and (Value <= Integer(High(TInstruction))) then begin
 | 
|---|
| 114 |       Instruction := TInstruction(Value);
 | 
|---|
| 115 |       InstructionInfo := InstructionSet.SearchInstruction(Instruction);
 | 
|---|
| 116 |       if Assigned(InstructionInfo) then begin
 | 
|---|
| 117 |         DecodedInstruction.Name := InstructionInfo.Name;
 | 
|---|
| 118 |         for J := 0 to Length(InstructionInfo.Params) - 1 do begin
 | 
|---|
| 119 |           if J > 0 then
 | 
|---|
| 120 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + ', ';
 | 
|---|
| 121 |           if InstructionInfo.Params[J] = ptNumberByte then begin
 | 
|---|
| 122 |             Value := Memory.ReadByte;
 | 
|---|
| 123 |             DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 124 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + IntToHex(Value, 2);
 | 
|---|
| 125 |           end else
 | 
|---|
| 126 |           if InstructionInfo.Params[J] = ptNumberWord then begin
 | 
|---|
| 127 |             Value := Memory.ReadWord;
 | 
|---|
| 128 |             DecodedInstruction.AddOpcode(Value shr 8);
 | 
|---|
| 129 |             DecodedInstruction.AddOpcode(Value and $ff);
 | 
|---|
| 130 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + IntToHex(Value, 4);
 | 
|---|
| 131 |           end else
 | 
|---|
| 132 |           if InstructionInfo.Params[J] = ptNumberByteIndir then begin
 | 
|---|
| 133 |             Value := Memory.ReadByte;
 | 
|---|
| 134 |             DecodedInstruction.AddOpcode(Value);
 | 
|---|
| 135 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + '(' + IntToHex(Value, 2) + ')';
 | 
|---|
| 136 |           end else
 | 
|---|
| 137 |           if InstructionInfo.Params[J] = ptNumberWordIndir then begin
 | 
|---|
| 138 |             Value := Memory.ReadWord;
 | 
|---|
| 139 |             DecodedInstruction.AddOpcode(Value shr 8);
 | 
|---|
| 140 |             DecodedInstruction.AddOpcode(Value and $ff);
 | 
|---|
| 141 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + '(' + IntToHex(Value, 4) + ')';
 | 
|---|
| 142 |           end else
 | 
|---|
| 143 |           if InstructionInfo.Params[J] in [ptRegA, ptRegB, ptRegC, ptRegD,
 | 
|---|
| 144 |             ptRegE, ptRegH, ptRegL, ptRegBC, ptRegDE, ptRegHL, ptRegSP,
 | 
|---|
| 145 |             ptFlagZ, ptFlagNZ, ptFlagC, ptFlagNC,
 | 
|---|
| 146 |             ptRegBCIndir, ptRegDEIndir, ptRegHLIndir,
 | 
|---|
| 147 |             pt00, pt08, pt10, pt18, pt20, pt28, pt30, pt38, pt0, pt1, pt2] then begin
 | 
|---|
| 148 |             DecodedInstruction.Parameters := DecodedInstruction.Parameters + ParamTypeText[InstructionInfo.Params[J]];
 | 
|---|
| 149 |           end else
 | 
|---|
| 150 |             raise Exception.Create('Unsupported instruction parameter type');
 | 
|---|
| 151 |         end;
 | 
|---|
| 152 |       end;
 | 
|---|
| 153 |     end;
 | 
|---|
| 154 |     DecodedInstructions.Add(DecodedInstruction);
 | 
|---|
| 155 |   end;
 | 
|---|
| 156 | end;
 | 
|---|
| 157 | 
 | 
|---|
| 158 | procedure TDisassembler.SaveToFile(FileName: string);
 | 
|---|
| 159 | var
 | 
|---|
| 160 |   Lines: TStringList;
 | 
|---|
| 161 | begin
 | 
|---|
| 162 |   Lines := TStringList.Create;
 | 
|---|
| 163 |   Disassemble;
 | 
|---|
| 164 |   ToLines(Lines);
 | 
|---|
| 165 |   Lines.SaveToFile(FileName);
 | 
|---|
| 166 |   FreeAndNil(Lines);
 | 
|---|
| 167 | end;
 | 
|---|
| 168 | 
 | 
|---|
| 169 | procedure TDisassembler.ToLines(Lines: TStrings);
 | 
|---|
| 170 | var
 | 
|---|
| 171 |   I: Integer;
 | 
|---|
| 172 |   OpcodesText: string;
 | 
|---|
| 173 | begin
 | 
|---|
| 174 |   Lines.Clear;
 | 
|---|
| 175 |   for I := 0 to DecodedInstructions.Count - 1 do
 | 
|---|
| 176 |   with TDecodedInstruction(DecodedInstructions[I]) do begin
 | 
|---|
| 177 |     OpcodesText := GetOpcodesText;
 | 
|---|
| 178 |     OpcodesText := OpcodesText + DupeString(' ', 13 - Length(OpcodesText));
 | 
|---|
| 179 |     Lines.Add(IntToHex(Address, 4) + ' ' + OpcodesText + ' ' + Name + ' ' + Parameters);
 | 
|---|
| 180 |   end;
 | 
|---|
| 181 | end;
 | 
|---|
| 182 | 
 | 
|---|
| 183 | constructor TDisassembler.Create;
 | 
|---|
| 184 | begin
 | 
|---|
| 185 |   InstructionSet := TInstructionSet.Create;
 | 
|---|
| 186 |   DecodedInstructions := TDecodedInstructions.Create;
 | 
|---|
| 187 | end;
 | 
|---|
| 188 | 
 | 
|---|
| 189 | destructor TDisassembler.Destroy;
 | 
|---|
| 190 | begin
 | 
|---|
| 191 |   FreeAndNil(DecodedInstructions);
 | 
|---|
| 192 |   FreeAndNil(InstructionSet);
 | 
|---|
| 193 |   inherited;
 | 
|---|
| 194 | end;
 | 
|---|
| 195 | 
 | 
|---|
| 196 | end.
 | 
|---|
| 197 | 
 | 
|---|