////////////////////////////////////////////////////////////////////// // // // 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 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} // // {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 // 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 := ''; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.SingleDataSwap: string; var Rn, Rd, Rm: byte; begin // Assembler syntax // {cond}{B} Rd,Rm,[Rn] // // where: // {cond} two-character condition mnemonic. // {B} if B is present then byte transfer, otherwise word transfer // Rd,Rm,Rn are expressions evaluating to valid register numbers // Parse the operands Rn := (w shr 16) and $F; Rd := (w shr 12) and $F; Rm := w and $F; if w and (1 shl 22) <> 0 then Result := Format('swp%5s%s,%s,[%s]', [condCodes[w shr 28]+'b', regCodes[Rd], regCodes[Rm], regCodes[Rn]]) else Result := Format('swp%5s%s,%s,[%s]', [condCodes[w shr 28], regCodes[Rd], regCodes[Rm], regCodes[Rn]]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.AluOperation(operand2: string): string; var Rn, Rd: byte; operand1: string; begin // Data Processing // The data processing instruction is only executed if the condition is true. // The instruction produces a result by performing a specified arithmetic or logical // operation on one or two operands. The first operand is always a register (Rn). Result := ''; // Parse the operands Rn := (w shr 16) and $F; Rd := (w shr 12) and $F; // Using R15 as an operand // If R15 (the PC) is used as an operand in a data processing instruction the register is // used directly. // // The PC value will be the address of the instruction, plus 8 or 12 bytes due to instruction // prefetching. If the shift amount is specified in the instruction, the PC will be 8 bytes // ahead. If a register is used to specify the shift amount the PC will be 12 bytes ahead. // Assembler syntax for data processing // 1 MOV, MVN (single operand instructions) // {cond}{S} Rd, // // 2 CMP, CMN, TEQ, TST (instructions which do not produce a result) // {cond} Rn, // // 3 AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, ORR, BIC // {cond}{S} Rd,Rn, // // where: // is Rm{,} or <#expression> // {cond} is a two-character condition mnemonic. // {S} set condition codes if S present (implied for CMP, CMN, TEQ, TST). // Rd, Rn and Rm are expressions evaluating to a register number. // <#expression> if this is used, the assembler will attempt to generate a shifted // immediate 8-bit field to match the expression. If this is // impossible, it will give an error. // is or #expression, or // RRX (rotate right one bit with extend). // s are: ASL, LSL, LSR, ASR, ROR. (ASL is a synonym for LSL, // they assemble to the same code) if w and (1 shl 20) <> 0 then begin operand1 := Format('%-4s %s, ', [condCodes[w shr 28]+'s', regCodes[Rd]]); case ((w shr 21) and $F) of MOV_OPCODE: Result := Format('mov%s%s', [operand1, operand2]); MVN_OPCODE: Result := Format('mvn%s%s', [operand1, operand2]); AND_OPCODE: Result := Format('and%s%s, %s', [operand1, regCodes[Rn], operand2]); EOR_OPCODE: Result := Format('eor%s%s, %s', [operand1, regCodes[Rn], operand2]); SUB_OPCODE: Result := Format('sub%s%s, %s', [operand1, regCodes[Rn], operand2]); RSB_OPCODE: Result := Format('rsb%s%s, %s', [operand1, regCodes[Rn], operand2]); ADD_OPCODE: Result := Format('add%s%s, %s', [operand1, regCodes[Rn], operand2]); ADC_OPCODE: Result := Format('adc%s%s, %s', [operand1, regCodes[Rn], operand2]); SBC_OPCODE: Result := Format('sbc%s%s, %s', [operand1, regCodes[Rn], operand2]); RSC_OPCODE: Result := Format('rsc%s%s, %s', [operand1, regCodes[Rn], operand2]); ORR_OPCODE: Result := Format('orr%s%s, %s', [operand1, regCodes[Rn], operand2]); BIC_OPCODE: Result := Format('bic%s%s, %s', [operand1, regCodes[Rn], operand2]); TST_OPCODE: Result := Format('tst%-5s%s, %s', [condCodes[w shr 28], regCodes[Rn], operand2]); TEQ_OPCODE: Result := Format('teq%-5s%s, %s', [condCodes[w shr 28], regCodes[Rn], operand2]); CMP_OPCODE: Result := Format('cmp%-5s%s, %s', [condCodes[w shr 28], regCodes[Rn], operand2]); CMN_OPCODE: Result := Format('cmn%-5s%s, %s', [condCodes[w shr 28], regCodes[Rn], operand2]); end; //Result := 'Fixme: ALU/PSR Rd=R15, S bit set, mode change not done yet!'; end else begin operand1 := Format('%-5s%s,', [condCodes[w shr 28], regCodes[Rd]]); case ((w shr 21) and $F) of MOV_OPCODE: Result := Format('mov%s %s', [operand1, operand2]); MVN_OPCODE: Result := Format('mvn%s %s', [operand1, operand2]); AND_OPCODE: Result := Format('and%s %s, %s', [operand1, regCodes[Rn], operand2]); EOR_OPCODE: Result := Format('eor%s %s, %s', [operand1, regCodes[Rn], operand2]); SUB_OPCODE: Result := Format('sub%s %s, %s', [operand1, regCodes[Rn], operand2]); RSB_OPCODE: Result := Format('rsb%s %s, %s', [operand1, regCodes[Rn], operand2]); ADD_OPCODE: Result := Format('add%s %s, %s', [operand1, regCodes[Rn], operand2]); ADC_OPCODE: Result := Format('adc%s %s, %s', [operand1, regCodes[Rn], operand2]); SBC_OPCODE: Result := Format('sbc%s %s, %s', [operand1, regCodes[Rn], operand2]); RSC_OPCODE: Result := Format('rsc%s %s, %s', [operand1, regCodes[Rn], operand2]); ORR_OPCODE: Result := Format('orr%s %s, %s', [operand1, regCodes[Rn], operand2]); BIC_OPCODE: Result := Format('bic%s %s, %s', [operand1, regCodes[Rn], operand2]); // Assembler syntax for PSR transfers // 1 MRS - transfer PSR contents to a register // MRS{cond} Rd, // // 2 MSR - transfer register contents to PSR // MSR{cond} ,Rm // // 3 MSR - transfer register contents to PSR flag bits only // MSR{cond} ,Rm // The most significant four bits of the register contents are written to the N,Z,C // & V flags respectively. // // 4 MSR - transfer immediate value to PSR flag bits only // MSR{cond} ,<#expression> // The expression should symbolise a 32 bit value of which the most significant // four bits are written to the N,Z,C and V flags respectively. // // Key: // {cond} two-character condition mnemonic. // Rd and Rm are expressions evaluating to a register number other than // R15 // is CPSR, CPSR_all, SPSR or SPSR_all. (CPSR and // CPSR_all are synonyms as are SPSR and SPSR_all) // is CPSR_flg or SPSR_flg // <#expression> where this is used, the assembler will attempt to generate a // shifted immediate 8-bit field to match the expression. If this is // impossible, it will give an error. TST_OPCODE: Result := Format('mrs%-5s%s, CPSR', [condCodes[w shr 28], regCodes[Rd]]); TEQ_OPCODE: if w and $FFFF0 = $9F000 then begin // MSR CPSR, Rm cond 00 0 10P1 0 1001 1111 0000 0000 [Rm], P=1 Result := Format('msr%-5sCPSR, %s', [condCodes[w shr 28], regCodes[w and $F]]); end else if w and $FF000 = $8F000 then begin // MSR CPSR, Rm cond 00 0 10P1 010001111 0000 0000 [Rm] // MSR CPSR, operand2 cond 00 1 10P1 010001111 Rotate Immedia Result := Format('msr%-5sCPSR_flg, %s', [condCodes[w shr 28], operand2]); end else Result := ''; CMP_OPCODE: Result := Format('mrs%-5s%s, SPSR', [condCodes[w shr 28], regCodes[Rd]]); CMN_OPCODE: if w and $FFFF0 = $9F000 then begin // MSR SPSR, Rm cond 00 0 10P1 0 1001 1111 0000 0000 [Rm], P=1 Result := Format('msr%-5sSPSR, %s', [condCodes[w shr 28], regCodes[w and $F]]); end else if w and $FF000 = $8F000 then begin // MSR SPSR, Rm cond 00 0 10P1 010001111 0000 0000 [Rm] // MSR SPSR, operand2 cond 00 1 10P1 010001111 Rotate Immedia Result := Format('msr%-5sSPSR_flg, %s', [condCodes[w shr 28], operand2]); end else Result := ''; end; end; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.SingleDataTransfer: string; const PRE_INCREMENT = 1 shl 24; FORWARD_BIT = 1 shl 23; var Rn, Rd, Rm: byte; index, operand2: string; preIncrement: boolean; addr: uint32; begin // Assembler syntax // {cond}{B}{T} Rd,
// // where: // LDR load from memory into a register // // STR store from a register into memory // // {cond} two-character condition mnemonic. // // {B} if B is present then byte transfer, otherwise word transfer // // {T} if T is present the W bit will be set in a post-indexed instruction, forcing // non-privileged mode for the transfer cycle. T is not allowed when a // pre-indexed addressing mode is specified or implied. // // Rd is an expression evaluating to a valid register number. // // Rn and Rm are expressions evaluating to a register number. If Rn is R15 then the // assembler will subtract 8 from the offset value to allow for ARM7TDMI // pipelining. In this case base write-back should not be specified. // //
can be: // An expression which generates an address: // // The assembler will attempt to generate an instruction using // the PC as a base and a corrected immediate offset to address // the location given by evaluating the expression. This will be a // PC relative, pre-indexed address. If the address is out of // range, an error will be generated. // // A pre-indexed addressing specification: // [Rn] offset of zero // [Rn,<#expression>]{!} offset of bytes // [Rn,{+/-}Rm{,}]{!} offset of +/- contents of index register, shifted by // // A post-indexed addressing specification: // [Rn],<#expression> offset of bytes // [Rn],{+/-}Rm{,} offset of +/- contents of index register, shifted as by // Single Data Transfer (LDR, STR) // The instruction is only executed if the condition is true. // The single data transfer instructions are used to load or store single bytes or words of // data. The memory address used in the transfer is calculated by adding an offset to or // subtracting an offset from a base register. // // Data aborts // A transfer to or from a legal address may cause problems for a memory management // system. For instance, in a system which uses virtual memory the required data may // be absent from main memory. The memory manager can signal a problem by taking // the processor ABORT input HIGH whereupon the Data Abort trap will be taken. It is // up to the system software to resolve the cause of the problem, then the instruction can // be restarted and the original program continued. // Parse the operands Rn := (w shr 16) and $F; Rd := (w shr 12) and $F; Rm := w and $F; index := regCodes[Rn]; // Offsets // The offset from the base may be either a 12 bit unsigned binary immediate value in // the instruction, or a second register (possibly shifted in some way). The offset may be // added to (U=1) or subtracted from (U=0) the base register Rn. The offset modification // may be performed either before (pre-indexed, P=1) or after (post-indexed, P=0) the // base is used as the transfer address. if w and (1 shl 25) = 0 then operand2 := Format('#$%x', [w and $FFF]) else operand2 := BarrelShifter(Rm, (w shr 5) and $3, Format('%d', [(w shr 7) and $1F])); // Are we indexing forwards or backwards preIncrement := w and PRE_INCREMENT <> 0; if operand2 <> '#0' then begin if preIncrement then begin if w and FORWARD_BIT <> 0 then index := Format('[%s, %s]', [index, operand2]) else index := Format('[%s, -%s]', [index, operand2]); if w and (1 shl 21) <> 0 then index := index + '!'; end else begin if w and FORWARD_BIT <> 0 then index := Format('[%s], %s', [index, operand2]) else index := Format('[%s], -%s', [index, operand2]); end; end else index := '[' + index + ']'; index := Format('%s, %s', [regCodes[Rd], index]); operand2 := condCodes[w shr 28]; if w and (1 shl 22) <> 0 then operand2 := operand2 + 'b'; // Check the L bit and see if its a load or store if w and (1 shl 20) <> 0 then begin if (Rn = R15) and (w and (1 shl 25) = 0) and not UseExactOpcodes then begin // It's a load 32 bit thingymajig addr := basePC+8; if w and FORWARD_BIT <> 0 then addr := addr + w and $FFF else addr := addr - w and $FFF; Result := Format('ldr%-5s%s, =$%.8x', [operand2, regCodes[Rd], vmReadWord(addr)]); end else Result := Format('ldr%-5s%s', [operand2, index]); end else Result := Format('str%-5s%s', [operand2, index]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.BlockDataTransfer: string; const LOAD_BIT = 1 shl 20; WRITEBACK_BIT = 1 shl 21; INCREMENT_BIT = 1 shl 23; BEFORE_BIT = 1 shl 24; var base: byte; i: integer; opcode: string; begin // Block Data Transfer (LDM, STM) // // Instruction cycle times // Normal LDM instructions take nS + 1N + 1I and LDM PC takes (n+1)S + 2N + 1I cycles, // where n is the number of words transferred. // // STM instructions take (n-1)S + 2N incremental cycles to execute, where n is the // number of words transferred. // // Assembler syntax // {cond} Rn{!},{^} // // where: // {cond} two character condition mnemonic. // // Rn is an expression evaluating to a valid register number // // is a list of registers and register ranges enclosed in {} (e.g. {R0,R2- // R7,R10}). // // {!} if present requests write-back (W=1), otherwise W=0 // // {^} if present set S bit to load the CPSR along with the PC, or force // transfer of user bank when in privileged mode // Addressing mode names // // There are different assembler mnemonics for each of the addressing modes, // depending on whether the instruction is being used to support stacks or for other // purposes. The equivalence between the names and the values of the bits in the // instruction are shown in the following table: // // FD, ED, FA, EA define pre/post indexing and the up/down bit by reference to the form // of stack required. The F and E refer to a “full” or “empty” stack, i.e. whether a pre-index // has to be done (full) before storing to the stack. The A and D refer to whether the stack // is ascending or descending. If ascending, a STM will go up and LDM down, if // descending, vice-versa. // // IA, IB, DA, DB allow control when LDM/STM are not being used for stacks and simply // mean Increment After, Increment Before, Decrement After, Decrement Before. // // Addressing mode names // Name Stack Other L bit P bit U bit // pre-increment load LDMED LDMIB 1 1 1 // post-increment load LDMFD LDMIA 1 0 1 // pre-decrement load LDMEA LDMDB 1 1 0 // post-decrement load LDMFA LDMDA 1 0 0 // pre-increment store STMFA STMIB 0 1 1 // post-increment store STMEA STMIA 0 0 1 // pre-decrement store STMFD STMDB 0 1 0 // post-decrement store STMED STMDA 0 0 0 // fixme: the decrement code here does not work in the same way the cpu really does // and can not be interrupted or aborted, it's results will not be correct // I reverse traverse the register list, and write the registers in reverse order, // so if halted I'll have written the wrong half of the registers to memory // shouldn't be a problem, since most of this should be atomic. For complete // accuracy, I should change this, in case I decide to implement better memory aborts base := (w shr 16) and $F; // Construct the register list Result := '{'; for i := 0 to 15 do if w and (1 shl i) <> 0 then if Result = '{' then Result := '{' + regCodes[i] else Result := Format('%s, %s', [Result, regCodes[i]]); Result := ', ' + Result + '}'; // Add a ! if it writes back the base if w and WRITEBACK_BIT <> 0 then Result := '!' + Result; Result := regCodes[base] + Result; // Its either a load or a store if w and LOAD_BIT <> 0 then opcode := 'ldm' else opcode := 'stm'; opcode := opcode + condCodes[w shr 28]; // Its either an increment or a decrement if w and INCREMENT_BIT <> 0 then opcode := opcode + 'i' else opcode := opcode + 'd'; // Either pre or post increment if w and BEFORE_BIT <> 0 then opcode := opcode + 'b' else opcode := opcode + 'a'; // Combine the two Result := Format('%-8s%s', [opcode, Result]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.SoftwareInterrupt: string; begin // Software Interrupt (SWI) // The instruction is only executed if the condition is true. // The software interrupt instruction is used to enter Supervisor mode in a controlled // manner. The instruction causes the software interrupt trap to be taken, which effects // the mode change. The PC is then forced to a fixed value (0x08) and the CPSR is // saved in SPSR_svc. If the SWI vector address is suitably protected (by external // memory management hardware) from modification by the user, a fully protected // operating system may be constructed. // // Return from the supervisor // The PC is saved in R14_svc upon entering the software interrupt trap, with the PC // adjusted to point to the word after the SWI instruction. MOVS PC,R14_svc will return // to the calling program and restore the CPSR. // // Note that the link mechanism is not re-entrant, so if the supervisor code wishes to use // software interrupts within itself it must first save a copy of the return address and // SPSR. // // Comment field // The bottom 24 bits of the instruction are ignored by the processor, and may be used // to communicate information to the supervisor code. For instance, the supervisor may // look at this field and use it to index into an array of entry points for routines which // perform the various supervisor functions. // // Instruction cycle times // Software interrupt instructions take 2S + 1N incremental cycles to execute // // Assembler syntax // SWI{cond} // // {cond} two character condition mnemonic // is evaluated and placed in the comment field (which is // ignored by ARM7TDMI). Result := Format('swi%-5s%s', [condCodes[w shr 28], SwiMap(w and $00FFFFFF)]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.SwiMap(swi: integer): string; var swic: integer; begin if not thumbMode then swic := swi shr 16 else swic := swi; if swic < $29 then Result := swiNames[swic] else Result := Format('#$%x', [swi]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.Multiply64: string; const SET_FLAGS_BIT = 1 shl 20; ACCUMULATE_BIT = 1 shl 21; SIGNED_OP_BIT = 1 shl 22; begin // Multiply Long and Multiply-Accumulate Long (MULL,MLAL) // The instruction is only executed if the condition is true. // The multiply long instructions perform integer multiplication on two 32 bit operands // and produce 64 bit results. Signed and unsigned multiplication each with optional // accumulate give rise to four variations. // // Instruction cycle times // MULL takes 1S + (m+1)I and MLAL 1S + (m+2)I cycles to execute, where m is the // number of 8 bit multiplier array cycles required to complete the multiply, which is // controlled by the value of the multiplier operand specified by Rs. // // m goes from 1 to 4 // // Assembler syntax // Mnemonic Description // ------------------------------ ----------------------------------- // UMULL{cond}{S} RdLo,RdHi,Rm,Rs Unsigned Multiply Long // UMLAL{cond}{S} RdLo,RdHi,Rm,Rs Unsigned Multiply & Accumulate Long // SMULL{cond}{S} RdLo,RdHi,Rm,Rs Signed Multiply Long // SMLAL{cond}{S} RdLo,RdHi,Rm,Rs Signed Multiply & Accumulate Long // // where: // {cond} two-character condition mnemonic. // // {S} set condition codes if S present // // RdLo, RdHi, Rm, Rs are expressions evaluating to a register number other // than R15. if w and SIGNED_OP_BIT <> 0 then Result := 's' else Result := 'u'; if w and ACCUMULATE_BIT <> 0 then Result := Result + 'mlal' else Result := Result + 'mull'; Result := Result + condCodes[w shr 28]; if w and SET_FLAGS_BIT <> 0 then Result := Result + 's'; Result := Format('%-8s %s,%s,%s,%s', [Result, regCodes[(w shr 12) and $F], regCodes[(w shr 16) and $F], regCodes[(w shr 8) and $F], regCodes[w and $F]]); end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.Multiply32: string; const SET_FLAGS_BIT = 1 shl 20; ACCUMULATE_BIT = 1 shl 21; begin // Multiply and Multiply-Accumulate (MUL, MLA) // Assembler syntax // MUL{cond}{S} Rd,Rm,Rs // MLA{cond}{S} Rd,Rm,Rs,Rn // // {cond} two-character condition mnemonic. // {S} set condition codes if S present // Rd, Rm, Rs and Rn are expressions evaluating to a register number other // than R15. // The multiply form of the instruction gives Rd:=Rm*Rs. Rn is ignored, and should be // set to zero for compatibility with possible future upgrades to the instruction set. // The multiply-accumulate form gives Rd:=Rm*Rs+Rn, which can save an explicit ADD // instruction in some circumstances. if w and ACCUMULATE_BIT <> 0 then Result := 'mla' else Result := 'mul'; Result := Result + condCodes[w shr 28]; // CPSR flags if w and SET_FLAGS_BIT <> 0 then Result := Result + 's'; // Construct the result Result := Format('%-8s%s, %s, %s', [Result, regCodes[(w shr 16) and $F], regCodes[w and $F], regCodes[(w shr 8) and $F]]); if w and ACCUMULATE_BIT <> 0 then Result := Result + ', ' + regCodes[(w shr 12) and $F]; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.HalfwordXfer: string; const LOAD_BIT = 1 shl 20; WRITEBACK_BIT = 1 shl 21; IMMEDIATE_OFFSET_BIT = 1 shl 22; UP_BIT = 1 shl 23; PREINCREMENT_BIT = 1 shl 24; var Rn, Rd, Rm: byte; opcode: string; begin // Assembler syntax // {cond} Rd,
// // LDR load from memory into a register // STR Store from a register into memory // {cond} two-character condition mnemonic. // H Transfer halfword quantity // SB Load sign extended byte (Only valid for LDR) // SH Load sign extended halfword (Only valid for LDR) // Rd is an expression evaluating to a valid register number. // //
can be: // An expression which generates an address: // // The assembler will attempt to generate an instruction using // the PC as a base and a corrected immediate offset to address // the location given by evaluating the expression. This will be a // PC relative, pre-indexed address. If the address is out of // range, an error will be generated. // // A pre-indexed addressing specification: // [Rn] offset of zero // [Rn,<#expression>]{!} offset of bytes // [Rn,{+/-}Rm]{!} offset of +/- contents of index register // // A post-indexed addressing specification: // [Rn],<#expression> offset of bytes // [Rn],{+/-}Rm offset of +/- contents of index register. // // Rn and Rm are expressions evaluating to a register number. // // If Rn is R15 then the assembler will subtract 8 from the offset // value to allow for ARM7TDMI pipelining. In this case base // write-back should not be specified. // // {!} writes back the base register (set the W bit) if ! is present. // Parse the operands Rm := w and $F; Rd := (w shr 12) and $F; Rn := (w shr 16) and $F; // Offset is either an 8-bit immediate or a register if w and IMMEDIATE_OFFSET_BIT <> 0 then Result := Format('#$%x', [(w shr 4) and $F0 + Rm]) else Result := regCodes[Rm]; // Offset is either added or subtracted from the base if w and UP_BIT = 0 then Result := '-' + Result; // It's either a store, load, or singed load if w and LOAD_BIT <> 0 then begin opcode := 'ldr' + condCodes[w shr 28]; case ((w shr 5) and $3) of 1: opcode := opcode + 'h'; 2: opcode := opcode + 'sb'; 3: opcode := opcode + 'sh'; else opcode := 'error'; end; end else begin opcode := 'str' + condCodes[w shr 28] + 'h'; end; opcode := Format('%-8s%s, [%s', [opcode, regCodes[Rd], regCodes[Rn]]); // Check the P bit and pre-increment if neccesary if w and PREINCREMENT_BIT <> 0 then begin // Stitch everything together Result := opcode + ', ' + Result + ']'; if w and WRITEBACK_BIT <> 0 then Result := Result + '!'; end else begin Result := opcode + '], ' + Result; end; end; ////////////////////////////////////////////////////////////////////// procedure TArm7Disass.UndefinedState(error: string); begin // error := Format('[%.8x] %s', [w, error]); // ShowMessage(error); end; ////////////////////////////////////////////////////////////////////// (*procedure TArm7Disass.LoadMapping(name: string); var fp: TextFile; line, token: string; base: string; begin if FileExists(name) then try // Open the mapper file AssignFile(fp, name); system.Reset(fp);//fixme // Read while not Eof(fp) do begin ReadLn(fp, line); token := CutLeft(line); if Uppercase(token) = 'BASE' then base := line else mapping.Add(base + token + '=' + line); end; finally // Close the mapper file CloseFile(fp); end; end;*) ////////////////////////////////////////////////////////////////////// function TArm7Disass.EmulateThumb: string; var c: byte; i, test: uint32; begin t := vmReadHalfword(basePC); was4bytes := false; // Inc(basePC, 2); c := t shr 8; case c of $00..$07: begin // Format 1: Move shifted register, [lsl Rd, Rs, #Offset5] Result := Format('lsl %s, %s, #%d', [regCodes[t and 7], regCodes[(t shr 3) and 7], (t shr 6) and 31]); end; $08..$0F: begin // Format 1: Move shifted register, [lsr Rd, Rs, #Offset5] Result := Format('lsr %s, %s, #%d', [regCodes[t and 7], regCodes[(t shr 3) and 7], (t shr 6) and 31]); end; $10..$17: begin // Format 1: Move shifted register, [asr Rd, Rs, #Offset5] Result := Format('asr %s, %s, #%d', [regCodes[t and 7], regCodes[(t shr 3) and 7], (t shr 6) and 31]); end; $18..$19: begin // Format 2: add, [add Rd, Rs, Rn] Result := Format('add %s, %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $1A..$1B: begin // Format 2: subtract, [sub Rd, Rs, Rn] Result := Format('sub %s, %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $1C..$1D: begin // Format 2: add, [add Rd, Rs, #Offset3] Result := Format('add %s, %s, #%d', [regCodes[t and 7], regCodes[(t shr 3) and 7], (t shr 6) and 7]); end; $1E..$1F: begin // Format 2: subtract, [sub Rd, Rs, #Offset3] Result := Format('sub %s, %s, #%d', [regCodes[t and 7], regCodes[(t shr 3) and 7], (t shr 6) and 7]); end; $20..$27: begin // Format 3: move immediate, [mov Rd, #Offset8] Result := Format('mov %s, #%d', [regCodes[c and 7], t and $FF]); end; $28..$2F: begin // Format 3: compare immediate, [cmp Rd, #Offset8] Result := Format('cmp %s, #%d', [regCodes[c and 7], t and $FF]); end; $30..$37: begin // Format 3: add immediate, [add Rd, #Offset8] Result := Format('add %s, #%d', [regCodes[c and 7], t and $FF]); end; $38..$3F: begin // Format 3: subtract immediate, [sub Rd, #Offset8] Result := Format('sub %s, #%d', [regCodes[c and 7], t and $FF]); end; $40..$43: begin // Format 4: ALU Operations (alu Rd, Rs) case (t shr 6) and $F of $0: Result := Format('and %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $1: Result := Format('eor %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $2: Result := Format('lsl %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $3: Result := Format('lsr %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $4: Result := Format('asr %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $5: Result := Format('adc %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $6: Result := Format('sbc %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $7: Result := Format('ror %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $8: Result := Format('tst %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $9: Result := Format('neg %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $A: Result := Format('cmp %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $B: Result := Format('cmn %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $C: Result := Format('orr %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $D: Result := Format('mul %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $E: Result := Format('bic %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); $F: Result := Format('mvn %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); end; end; $44: begin // Format 5: Hi register add, [add Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: Result := Format('add %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7 + 8]]); 2: Result := Format('add %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7]]); 3: Result := Format('add %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7 + 8]]); else UndefinedState('Thumb format 5: H1 and H2 = 0'); end; end; $45: begin // Format 5: Hi register subtract, [cmp Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: Result := Format('cmp %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7 + 8]]); 2: Result := Format('cmp %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7]]); 3: Result := Format('cmp %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7 + 8]]); else UndefinedState('Thumb format 5: H1 and H2 = 0'); end; end; $46: begin // Format 5: Hi register move, [mov Rd/Hd, Hs/Rs] (both low is undefined) case (t shr 6) and 3 of 1: Result := Format('mov %s, %s', [regCodes[t and 7], regCodes[(t shr 3) and 7 + 8]]); 2: Result := Format('mov %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7]]); 3: Result := Format('mov %s, %s', [regCodes[t and 7 + 8], regCodes[(t shr 3) and 7 + 8]]); else UndefinedState('Thumb format 5: H1 and H2 = 0'); end; end; $47: begin // Format 5: Branch and exchange, [bx Rs/Hs] (H1=1 is undefined) case (t shr 6) and 3 of 0: Result := Format('bx %s', [regCodes[(t shr 3) and 7]]); 1: Result := Format('bx %s', [regCodes[(t shr 3) and 7 + 8]]); else UndefinedState('Thumb format 5: BX with H1 = 1'); end; end; $48..$4F: begin // Format 6: PC-relative load, [ldr Rd, [PC, #Imm]] if UseExactOpcodes then Result := Format('ldr %s, [PC, %d]', [regCodes[c and 7], (t and $FF) shl 2]) else Result := Format('ldr %s, =$%8.8x', [regCodes[c and 7], vmReadWord((basePC + 4) and $FFFFFFFC + (t and $FF) shl 2)]); end; $50..$51: begin // Format 7: Store with register offset, [str Rd, [Rb, Ro]] Result := Format('str %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $52..$53: begin // Format 8: Store halfword, [strh Rd, [Rb, Ro]] Result := Format('strh %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $54..$55: begin // Format 7: Store byte with register offset, [strb Rd, [Rb, Ro]] Result := Format('strb %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $56..$57: begin // Format 8: Load signed byte, [ldrsb Rd, [Rb, Ro]] Result := Format('ldrsb %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $58..$59: begin // Format 7: Load with register offset, [ldr Rd, [Rb, Ro]] Result := Format('ldr %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $5A..$5B: begin // Format 8: Load halfword, [ldrh Rd, [Rb, Ro]] Result := Format('ldrh %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $5C..$5D: begin // Format 7: Load byte with register offset, [ldrb Rd, [Rb, Ro]] Result := Format('ldrb %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $5E..$5F: begin // Format 8: Load signed halfword, [ldrsh Rd, [Rb, Ro]] Result := Format('ldrsh %s, [%s, %s]', [regCodes[t and 7], regCodes[(t shr 3) and 7], regCodes[(t shr 6) and 7]]); end; $60..$67: begin // Format 9: Store with immediate offset, [str Rd, [Rb, #Imm]] test := ((t shr 6) and $1F) shl 2; Result := Format('str %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $68..$6F: begin // Format 9: Load with immediate offset, [ldr Rd, [Rb, #Imm]] test := ((t shr 6) and $1F) shl 2; Result := Format('ldr %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $70..$77: begin // Format 9: Store byte with immediate offset, [strb Rd, [Rb, #Imm]] test := (t shr 6) and $1F; Result := Format('strb %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $78..$7F: begin // Format 9: Load byte with immediate offset, [ldrb Rd, [Rb, #Imm]] test := (t shr 6) and $1F; Result := Format('ldrb %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $80..$87: begin // Format 10: Store halfword, [strh Rd, [Rb, #Imm]] test := ((t shr 6) and $1F) shl 1; Result := Format('strh %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $88..$8F: begin // Format 10: Load halfword, [ldrh Rd, [Rb, #Imm]] test := ((t shr 6) and $1F) shl 1; Result := Format('ldrh %s, [%s', [regCodes[t and 7], regCodes[(t shr 3) and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $90..$97: begin // Format 11: SP-relative store, [str Rd, [SP, #Imm]] test := (t and $FF) shl 2; Result := Format('str %s, [sp', [regCodes[c and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $98..$9F: begin // Format 11: SP-relative load, [ldr Rd, [SP, #Imm]] test := (t and $FF) shl 2; Result := Format('ldr %s, [sp', [regCodes[c and 7]]); if test <> 0 then Result := Format('%s, #%d]', [Result, test]) else Result := Result + ']'; end; $A0..$A7: begin // Format 12: Load address, [add Rd, PC, #Imm] Result := Format('add %s, pc, #%d', [regCodes[c and 7], (t and $FF) shl 2]); end; $A8..$AF: begin // Format 12: Load address, [add Rd, SP, #Imm] Result := Format('add %s, sp, #%d', [regCodes[c and 7], (t and $FF) shl 2]); end; $B0: begin // Format 13: Add offset to stack pointer [add SP, #+/-Imm] if t and (1 shl 7) <> 0 then Result := Format('sub sp, #%d', [(t and $7F) shl 2]) else Result := Format('add sp, #%d', [(t and $7F) shl 2]); end; $B1..$B3: begin // Udefined Opcode // UndefinedState(Format('Thumb opcode: %4.4x', [t])); end; $B4: begin // Format 14: push {rlist} Result := 'push {'; for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; if Result[Length(Result)] <> '{' then Delete(Result, Length(Result)-1, 2); Result := Result + '}'; end; $B5: begin // Format 14: push {rlist,LR} Result := 'push {'; for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; Result := Result + 'lr}' end; $B6..$BB: begin // Undefined Opcode // UndefinedState(Format('Thumb opcode: %4.4x', [t])); end; $BC: begin // Format 14: pop {rlist} Result := 'pop {'; for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; if Result[Length(Result)] <> '{' then Delete(Result, Length(Result)-1, 2); Result := Result + '}'; end; $BD: begin // Format 14: pop {rlist, PC} Result := 'pop {'; for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; Result := Result + 'pc}' end; $BE..$BF: begin // Undefined Opcode // UndefinedState(Format('Thumb opcode: %4.4x', [t])); end; $C0..$C7: begin // Format 15: stmia Rb!, {rlist} Result := Format('stmia %s!, {', [regCodes[c and 7]]); for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; if Result[Length(Result)] <> '{' then Delete(Result, Length(Result)-1, 2); Result := Result + '}'; end; $C8..$CF: begin // Format 15: ldmia Rb!, {rlist} Result := Format('ldmia %s!, {', [regCodes[c and 7]]); for i := 0 to 7 do if t and (1 shl i) <> 0 then begin Result := Result + regCodes[i] + ', '; end; if Result[Length(Result)] <> '{' then Delete(Result, Length(Result)-1, 2); Result := Result + '}'; end; $D0..$DE: begin // Format 16: Conditionial branch, [bXX label] test := t and $FF; if t and (1 shl 7) <> 0 then test := test or $FFFFFF00; Result := Format('b%s %s', [condCodes[c and $F], AddressMap(basePC+4+test shl 1)]); end; $DF: begin // Format 17: Software Interrupt, [swi value8] Result := Format('swi %s', [SwiMap(t and $FF)]); end; $E0..$E7: begin // Format 18: Unconditional branch, [b label] test := t and $7FF; if t and (1 shl 10) <> 0 then test := test or $FFFFF800; Result := Format('b %s', [AddressMap(basePC+4+test shl 1)]); end; $E8..$EF: begin // Undefined Opcode // UndefinedState(Format('Thumb opcode: %4.4x', [t])); end; $F0..$F7: begin // Format 19: Long branch with link (hw1), [part one of bl label] w := t shl 16 + vmReadHalfword(basePC+2); was4bytes := true; test := t and $7FF; if t and (1 shl 10) <> 0 then test := test or $FFFFF800; test := basePC + 4 + test shl 12; test := test + (vmReadHalfword(basePC+2) and $7FF) shl 1; inc(basePC, 2); Result := Format('bl %s', [AddressMap(test)]); end; $F8..$FF: begin // Format 19: Long branch with link (hw2), [part two of bl label] Dec(basePC, 2); // dirty, untested! fixme todo note if not recHack then begin recHack := true; Result := EmulateThumb; end; if not was4bytes then begin Inc(basePC, 2); Result := ''; end; // Result := ''; end; end; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.EmulateArm: string; var rm, rs, test, shift: uint32; operand2: string; begin // In ARM mode, so read the current instruction in a word w := vmReadWord(basePC); // Process the rest of the instruction if the condition is true if w and BX_MASK = BX_SIG then begin Result := BranchAndExchange; end else if w and BRANCH_MASK = BRANCH_SIG then begin Result := Branch; end else if w and MULTIPLY_MASK = MULTIPLY_SIG then begin Result := Multiply32; end else if w and MULTIPLY_LONG_MASK = MULTIPLY_LONG_SIG then begin Result := Multiply64; end else if w and ALU_SHIFT_BY_IMM_MASK = ALU_SHIFT_BY_IMM_SIG then begin // Instruction specified shift amount // When the shift amount is specified in the instruction, it is contained in a 5 bit field which // may take any value from 0 to 31. operand2 := BarrelShifter(w and $F, (w shr 5) and $3, IntToStr((w shr 7) and $1F)); // Perform the alu operation Result := AluOperation(operand2); end else if w and ALU_SHIFT_BY_REG_MASK = ALU_SHIFT_BY_REG_SIG then begin // Register specified shift amount Rm := w and $F; Rs := (w shr 8) and $F; if Rs = R15 then UndefinedState('Data Processing: Rs = R15'); // Perform the data processing operation Result := AluOperation(BarrelShifter(Rm, (w shr 5) and $3, regCodes[Rs])); end else if w and ALU_IMM_ROT_MASK = ALU_IMM_ROT_SIG then begin // Immediate operand rotates // The immediate operand rotate field is a 4 bit unsigned integer which specifies a shift // operation on the 8 bit immediate value. This value is zero extended to 32 bits, and then // subject to a rotate right by twice the value in the rotate field. This enables many // common constants to be generated, for example all powers of 2. shift := ((w shr 8) and $F) shl 1; test := w and $FF; test := (test shr shift) or (test shl (32-shift)); operand2 := Format('#$%x', [test]); Result := AluOperation(operand2); end else if w and SINGLE_DATA_SWAP_MASK = SINGLE_DATA_SWAP_SIG then Result := SingleDataSwap else if w and HW_XFER_REGOFS_MASK = HW_XFER_REGOFS_SIG then Result := HalfwordXfer else if w and HW_XFER_IMMOFS_MASK = HW_XFER_IMMOFS_SIG then Result := HalfwordXfer else if w and UNDEFINED_MASK = UNDEFINED_SIG then Result := UndefinedOpcode else if w and SINGLE_DATA_XFER_MASK = SINGLE_DATA_XFER_SIG then Result := SingleDataTransfer else if w and BLOCK_DATA_XFER_MASK = BLOCK_DATA_XFER_SIG then Result := BlockDataTransfer else if w and SWI_MASK = SWI_SIG then Result := SoftwareInterrupt else if w and COPRO_DATA_XFER_MASK = COPRO_DATA_XFER_SIG then Result := UndefinedOpcode else if w and COPRO_DATA_OP_MASK = COPRO_DATA_OP_SIG then Result := UndefinedOpcode else if w and COPRO_REG_XFER_MASK = COPRO_REG_XFER_SIG then Result := UndefinedOpcode else UndefinedState('Instruction Decoder: Does not match any instruction class'); end; ////////////////////////////////////////////////////////////////////// procedure TArm7Disass.SetSpacer(tabs: boolean); begin FUseTabs := tabs; if FUseTabs then spacer := #9 else spacer := ' '; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.AddressMap(addr: uint32): string; var addrP: uint32; begin addrP := addr and not 1; Result := addresses.Values[IntToStr(addrP)]; if Result = '' then Result := '#$' + IntToHex(addr, 8) else begin if addr and 1 <> 0 then Result := Result + '|1'; end; end; ////////////////////////////////////////////////////////////////////// function TArm7Disass.AddressMapNoPre(addr: uint32; st: string): string; begin Result := addresses.Values[IntToStr(addr)]; if Result = '' then begin Result := IntToHex(addr, 8); if showJunk then Result := Result + spacer + st; end; Result := Result + StringOfChar(' ', 18-Length(Result)); end; ////////////////////////////////////////////////////////////////////// procedure TArm7Disass.AddMapping(address: uint32; name: string); begin addresses.Add(IntToStr(address) + '=' + name); end; ////////////////////////////////////////////////////////////////////// procedure TArm7Disass.ClearMappings; begin addresses.Clear; end; ////////////////////////////////////////////////////////////////////// procedure TArm7Disass.RemoveMapping(address: uint32); var index: integer; begin index := addresses.IndexOfName(IntToStr(address)); if index > -1 then addresses.Delete(index); end; ////////////////////////////////////////////////////////////////////// initialization disassembler := TArm7Disass.Create; finalization disassembler.Free; end. //////////////////////////////////////////////////////////////////////