⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 arm_disass.pas

📁 一个不出名的GBA模拟器
💻 PAS
📖 第 1 页 / 共 2 页
字号:
//////////////////////////////////////////////////////////////////////
//                                                                  //
// 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 + -