📄 arm_disass.pas
字号:
//////////////////////////////////////////////////////////////////////
// //
// arm_disass.pas: ARM7tdmi disassembler //
// //
// The contents of this file are subject to the Bottled Light //
// Public License Version 1.0 (the "License"); you may not use this //
// file except in compliance with the License. You may obtain a //
// copy of the License at http://www.bottledlight.com/BLPL/ //
// //
// Software distributed under the License is distributed on an //
// "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or //
// implied. See the License for the specific language governing //
// rights and limitations under the License. //
// //
// The Original Code is the Mappy VM User Interface, released //
// April 1st, 2003. The Initial Developer of the Original Code is //
// Bottled Light, Inc. Portions created by Bottled Light, Inc. are //
// Copyright (C) 2001-2003 Bottled Light, Inc. All Rights Reserved. //
// //
// Author(s): //
// Michael Noland (joat), michael@bottledlight.com //
// //
// Changelog: //
// 1.0: First public release (April 1st, 2003) //
// //
// Notes: //
// None at present. //
// //
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
unit arm_disass; /////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
interface ////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
uses
Classes, SysUtils,
nexus, AddressSpace;
//////////////////////////////////////////////////////////////////////
type
TArm7Disass = class
private
basePC: uint32;
w: uint32;
t: uint16;
was4bytes: boolean;
spacer: string;
addresses: TStringList;
// ARM hardware helpers
function BarrelShifter(number, shiftType: byte; shift: string): string;
// ARM opcode processors
function Branch: string;
function BranchAndExchange: string;
function UndefinedOpcode: string;
function SingleDataSwap: string;
function AluOperation(operand2: string): string;
function SingleDataTransfer: string;
function BlockDataTransfer: string;
function SoftwareInterrupt: string;
function Multiply64: string;
function Multiply32: string;
function HalfwordXfer: string;
// Opcode decoder
procedure UndefinedState(error: string);
procedure SetSpacer(tabs: boolean);
function AddressMap(addr: uint32): string;
function AddressMapNoPre(addr: uint32; st: string): string;
function SwiMap(swi: integer): string;
public
thumbMode, showJunk: boolean;
FUseTabs: boolean;
UseExactOpcodes: boolean;
property useTabs: boolean read FUseTabs write SetSpacer;
procedure Emulate(startPC: uint32; count: integer; decoded: TStringList);
constructor Create;
destructor Destroy; override;
function EmulateThumb: string;
function EmulateArm: string;
procedure AddMapping(address: uint32; name: string);
procedure RemoveMapping(address: uint32);
procedure ClearMappings;
end;
//////////////////////////////////////////////////////////////////////
var
disassembler: TArm7Disass;
//////////////////////////////////////////////////////////////////////
implementation ///////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
const
condCodes: array[0..15] of string =
('eq', 'ne', 'cs', 'cc', 'mi', 'pl', 'vs', 'vc',
'hi', 'ls', 'ge', 'lt', 'gt', 'le', '', 'nv');
regCodes: array[0..15] of string =
('r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7',
'r8', 'r9', 'r10', 'r11', 'r12', 'sp', 'lr', 'pc');
//////////////////////////////////////////////////////////////////////
var
recHack: boolean = false;
//////////////////////////////////////////////////////////////////////
constructor TArm7Disass.Create;
begin
thumbMode := false;
was4bytes := false;
showJunk := true;
useTabs := false;
addresses := TStringList.Create;
end;
//////////////////////////////////////////////////////////////////////
destructor TArm7Disass.Destroy;
begin
addresses.Free;
inherited;
end;
//////////////////////////////////////////////////////////////////////
// This function emulates the barrel shifter, taking a number, shift
// type, and shift amount, and returning the shifted number. It also
// sets the private barrelCarry to the carry-out of the barrel shifter
function TArm7Disass.BarrelShifter(number, shiftType: byte; shift: string): string;
begin
case shiftType of
LSL: begin
// A logical shift left (LSL) takes the contents of Rm and
// moves each bit by the specified amount to a more significant position. The least
// significant bits of the result are filled with zeros, and the high bits of Rm which do not
// map into the result are discarded, except that the least significant discarded bit
// becomes the shifter carry output which may be latched into the C bit of the CPSR when
// the ALU operation is in the logical class (see above).
// Note LSL #0 is a special case, where the shifter carry out is the old value of the CPSR C
// flag. The contents of Rm are used directly as the second operand.
Result := regCodes[number];
if shift <> '0' then begin
if Upcase(shift[1]) = 'R' then
Result := Result + ', LSL ' + shift
else
Result := Result + ', LSL #' + shift;
end;
end;
LSR: begin
// A logical shift right (LSR) is similar, but the contents of Rm are moved to less
// significant positions in the result.
//
// The form of the shift field which might be expected to correspond to LSR #0 is used to
// encode LSR #32, which has a zero result with bit 31 of Rm as the carry output. Logical
// shift right zero is redundant as it is the same as logical shift left zero, so the assembler
// will convert LSR #0 (and ASR #0 and ROR #0) into LSL #0, and allow LSR #32 to be
// specified.
if shift = '0' then shift := '32';
Result := regCodes[number];
if Upcase(shift[1]) = 'R' then
Result := Result + ', LSR ' + shift
else
Result := Result + ', LSR #' + shift;
end;
ASR: begin
// An arithmetic shift right (ASR) is similar to logical shift right, except that the high bits
// are filled with bit 31 of Rm instead of zeros. This preserves the sign in 2's complement
// notation.
//
// The form of the shift field which might be expected to give ASR #0 is used to encode
// ASR #32. Bit 31 of Rm is again used as the carry output, and each bit of operand 2 is
// also equal to bit 31 of Rm. The result is therefore all ones or all zeros, according to the
// value of bit 31 of Rm.
if shift = '0' then shift := '32';
Result := regCodes[number];
if Upcase(shift[1]) = 'R' then
Result := Result + ', ASR ' + shift
else
Result := Result + ', ASR #' + shift;
end;
ROR: begin
// Rotate right (ROR) operations reuse the bits which overshoot in a logical shift right
// operation by reintroducing them at the high end of the result, in place of the zeros used
// to fill the high end in logical right operations.
//
// The form of the shift field which might be expected to give ROR #0 is used to encode
// a special function of the barrel shifter, rotate right extended (RRX). This is a rotate right
// by one bit position of the 33 bit quantity formed by appending the CPSR C flag to the
// most significant end of the contents of Rm.
Result := regCodes[number];
if shift = '0' then
Result := Result + ', RRX'
else begin
if Upcase(shift[1]) = 'R' then
Result := Result + ', ROR ' + shift
else
Result := Result + ', ROR #' + shift;
end;
end;
end;
end;
//////////////////////////////////////////////////////////////////////
// This function decodes instructions until at least <cycles> have passed
procedure TArm7Disass.Emulate(startPC: uint32; count: integer; decoded: TStringList);
var
line: string;
begin
basePC := startPC;
t := 0;
w := 0;
while count > 0 do begin
// Decode the instruction
if thumbMode then begin
line := EmulateThumb;
if was4bytes then begin
Dec(basePC, 2);
decoded.AddObject(Format('%8s%s%s', [AddressMapNoPre(basePC, IntToHex(w, 8)), spacer, line]), TObject(basePC));
Inc(basePC, 4);
end else begin
decoded.AddObject(Format('%8s%s%s', [AddressMapNoPre(basePC, IntToHex(t, 4)), spacer, line]), TObject(basePC));
Inc(basePC, 2);
end;
end else begin
line := EmulateArm;
decoded.AddObject(Format('%8s%s%s', [AddressMapNoPre(basePC, IntToHex(w, 8)), spacer, line]), TObject(basePC));
Inc(basePC, 4);
end;
// Decrement the count
Dec(count);
end;
end;
//////////////////////////////////////////////////////////////////////
// Branch and Branch with Link (B, BL)
function TArm7Disass.Branch: string;
var
operand2: uint32;
begin
// Branch with Link (BL) writes the old PC into the link register (R14) of the current bank.
// The PC value written into R14 is adjusted to allow for the prefetch, and contains the
// address of the instruction following the branch and link instruction. Note that the CPSR
// is not saved with the PC and R14[1:0] are always cleared.
// Branch instructions contain a signed 2's complement 24 bit offset. This is shifted left
// two bits, sign extended to 32 bits, and added to the PC. The instruction can therefore
// specify a branch of +/- 32Mbytes. The branch offset must take account of the prefetch
// operation, which causes the PC to be 2 words (8 bytes) ahead of the current
// instruction.
operand2 := (w and $FFFFFF) shl 2;
if operand2 and (1 shl 25) <> 0 then operand2 := $FC000000 or operand2;
// Assembler syntax
// B{L}{cond} <expression>
//
// {L} is used to request the Branch with Link form of the instruction.
// If absent, R14 will not be affected by the instruction.
// {cond} is a two-character condition mnemonic
// <expression> is the destination. The assembler calculates the offset.
if w and (1 shl 24) <> 0 then begin
Result := Format('bl%-6s%s', [condCodes[w shr 28], AddressMap(basePC + 8 + operand2)]);
end else begin
Result := Format('b%-7s%s', [condCodes[w shr 28], AddressMap(basePC + 8 + operand2)]);
end;
end;
//////////////////////////////////////////////////////////////////////
// Branch and Exchange (BX)
function TArm7Disass.BranchAndExchange: string;
begin
// Assembler syntax
// BX - branch and exchange.
// BX{cond} Rn
//
// {cond} is a two character condition mnemonic.
// Rn is an expression evaluating to a valid register number.
Result := Format('bx%-6s%s', [condCodes[w shr 28], regCodes[w and $F]]);
end;
//////////////////////////////////////////////////////////////////////
function TArm7Disass.UndefinedOpcode: string;
begin
// Undefined Instruction
// The instruction is only executed if the condition is true.
// If the condition is true, the undefined instruction trap will be taken.
//
// Note that the undefined instruction mechanism involves offering this instruction to any
// coprocessors which may be present, and all coprocessors must refuse to accept it by
// driving CPA and CPB HIGH.
//
// Instruction cycle times
// This instruction takes 2S + 1I + 1N cycles.
//
// Assembler syntax
// The assembler has no mnemonics for generating this instruction. If it is adopted in the
// future for some specified use, suitable mnemonics will be added to the assembler.
//
// Until such time, this instruction must not be used.
// if FStopOnUndefindOpcode then begin
// raise EUndefinedOpcode.Create(st);
// end;
Result := '<Undefined Opcode>';
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -