📄 armemu.c
字号:
/* armemu.c -- Main instruction emulation: ARM7 Instruction Emulator. Copyright (C) 1994 Advanced RISC Machines Ltd. Modifications to add arch. v4 support by <jsmith@cygnus.com>. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include "armdefs.h"#include "armemu.h"#include "armos.h"#include "iwmmxt.h"static ARMword GetDPRegRHS (ARMul_State *, ARMword);static ARMword GetDPSRegRHS (ARMul_State *, ARMword);static void WriteR15 (ARMul_State *, ARMword);static void WriteSR15 (ARMul_State *, ARMword);static void WriteR15Branch (ARMul_State *, ARMword);static ARMword GetLSRegRHS (ARMul_State *, ARMword);static ARMword GetLS7RHS (ARMul_State *, ARMword);static unsigned LoadWord (ARMul_State *, ARMword, ARMword);static unsigned LoadHalfWord (ARMul_State *, ARMword, ARMword, int);static unsigned LoadByte (ARMul_State *, ARMword, ARMword, int);static unsigned StoreWord (ARMul_State *, ARMword, ARMword);static unsigned StoreHalfWord (ARMul_State *, ARMword, ARMword);static unsigned StoreByte (ARMul_State *, ARMword, ARMword);static void LoadMult (ARMul_State *, ARMword, ARMword, ARMword);static void StoreMult (ARMul_State *, ARMword, ARMword, ARMword);static void LoadSMult (ARMul_State *, ARMword, ARMword, ARMword);static void StoreSMult (ARMul_State *, ARMword, ARMword, ARMword);static unsigned Multiply64 (ARMul_State *, ARMword, int, int);static unsigned MultiplyAdd64 (ARMul_State *, ARMword, int, int);static void Handle_Load_Double (ARMul_State *, ARMword);static void Handle_Store_Double (ARMul_State *, ARMword);#define LUNSIGNED (0) /* unsigned operation */#define LSIGNED (1) /* signed operation */#define LDEFAULT (0) /* default : do nothing */#define LSCC (1) /* set condition codes on result */#ifdef NEED_UI_LOOP_HOOK/* How often to run the ui_loop update, when in use. */#define UI_LOOP_POLL_INTERVAL 0x32000/* Counter for the ui_loop_hook update. */static long ui_loop_hook_counter = UI_LOOP_POLL_INTERVAL;/* Actual hook to call to run through gdb's gui event loop. */extern int (*deprecated_ui_loop_hook) (int);#endif /* NEED_UI_LOOP_HOOK */extern int stop_simulator;/* Short-hand macros for LDR/STR. *//* Store post decrement writeback. */#define SHDOWNWB() \ lhs = LHS ; \ if (StoreHalfWord (state, instr, lhs)) \ LSBase = lhs - GetLS7RHS (state, instr);/* Store post increment writeback. */#define SHUPWB() \ lhs = LHS ; \ if (StoreHalfWord (state, instr, lhs)) \ LSBase = lhs + GetLS7RHS (state, instr);/* Store pre decrement. */#define SHPREDOWN() \ (void)StoreHalfWord (state, instr, LHS - GetLS7RHS (state, instr));/* Store pre decrement writeback. */#define SHPREDOWNWB() \ temp = LHS - GetLS7RHS (state, instr); \ if (StoreHalfWord (state, instr, temp)) \ LSBase = temp;/* Store pre increment. */#define SHPREUP() \ (void)StoreHalfWord (state, instr, LHS + GetLS7RHS (state, instr));/* Store pre increment writeback. */#define SHPREUPWB() \ temp = LHS + GetLS7RHS (state, instr); \ if (StoreHalfWord (state, instr, temp)) \ LSBase = temp;/* Load post decrement writeback. */#define LHPOSTDOWN() \{ \ int done = 1; \ lhs = LHS; \ temp = lhs - GetLS7RHS (state, instr); \ \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \ LSBase = temp; \ break; \ case 2: /* SB */ \ if (LoadByte (state, instr, lhs, LSIGNED)) \ LSBase = temp; \ break; \ case 3: /* SH */ \ if (LoadHalfWord (state, instr, lhs, LSIGNED)) \ LSBase = temp; \ break; \ case 0: /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* Load post increment writeback. */#define LHPOSTUP() \{ \ int done = 1; \ lhs = LHS; \ temp = lhs + GetLS7RHS (state, instr); \ \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ if (LoadHalfWord (state, instr, lhs, LUNSIGNED)) \ LSBase = temp; \ break; \ case 2: /* SB */ \ if (LoadByte (state, instr, lhs, LSIGNED)) \ LSBase = temp; \ break; \ case 3: /* SH */ \ if (LoadHalfWord (state, instr, lhs, LSIGNED)) \ LSBase = temp; \ break; \ case 0: /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* Load pre decrement. */#define LHPREDOWN() \{ \ int done = 1; \ \ temp = LHS - GetLS7RHS (state, instr); \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ (void) LoadHalfWord (state, instr, temp, LUNSIGNED); \ break; \ case 2: /* SB */ \ (void) LoadByte (state, instr, temp, LSIGNED); \ break; \ case 3: /* SH */ \ (void) LoadHalfWord (state, instr, temp, LSIGNED); \ break; \ case 0: \ /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* Load pre decrement writeback. */#define LHPREDOWNWB() \{ \ int done = 1; \ \ temp = LHS - GetLS7RHS (state, instr); \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \ LSBase = temp; \ break; \ case 2: /* SB */ \ if (LoadByte (state, instr, temp, LSIGNED)) \ LSBase = temp; \ break; \ case 3: /* SH */ \ if (LoadHalfWord (state, instr, temp, LSIGNED)) \ LSBase = temp; \ break; \ case 0: \ /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* Load pre increment. */#define LHPREUP() \{ \ int done = 1; \ \ temp = LHS + GetLS7RHS (state, instr); \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ (void) LoadHalfWord (state, instr, temp, LUNSIGNED); \ break; \ case 2: /* SB */ \ (void) LoadByte (state, instr, temp, LSIGNED); \ break; \ case 3: /* SH */ \ (void) LoadHalfWord (state, instr, temp, LSIGNED); \ break; \ case 0: \ /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* Load pre increment writeback. */#define LHPREUPWB() \{ \ int done = 1; \ \ temp = LHS + GetLS7RHS (state, instr); \ switch (BITS (5, 6)) \ { \ case 1: /* H */ \ if (LoadHalfWord (state, instr, temp, LUNSIGNED)) \ LSBase = temp; \ break; \ case 2: /* SB */ \ if (LoadByte (state, instr, temp, LSIGNED)) \ LSBase = temp; \ break; \ case 3: /* SH */ \ if (LoadHalfWord (state, instr, temp, LSIGNED)) \ LSBase = temp; \ break; \ case 0: \ /* SWP handled elsewhere. */ \ default: \ done = 0; \ break; \ } \ if (done) \ break; \}/* EMULATION of ARM6. *//* The PC pipeline value depends on whether ARM or Thumb instructions are being executed. */ARMword isize;ARMword#ifdef MODE32ARMul_Emulate32 (ARMul_State * state)#elseARMul_Emulate26 (ARMul_State * state)#endif{ ARMword instr; /* The current instruction. */ ARMword dest = 0; /* Almost the DestBus. */ ARMword temp; /* Ubiquitous third hand. */ ARMword pc = 0; /* The address of the current instruction. */ ARMword lhs; /* Almost the ABus and BBus. */ ARMword rhs; ARMword decoded = 0; /* Instruction pipeline. */ ARMword loaded = 0; /* Execute the next instruction. */ if (state->NextInstr < PRIMEPIPE) { decoded = state->decoded; loaded = state->loaded; pc = state->pc; } do { /* Just keep going. */ isize = INSN_SIZE; switch (state->NextInstr) { case SEQ: /* Advance the pipeline, and an S cycle. */ state->Reg[15] += isize; pc += isize; instr = decoded; decoded = loaded; loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize); break; case NONSEQ: /* Advance the pipeline, and an N cycle. */ state->Reg[15] += isize; pc += isize; instr = decoded; decoded = loaded; loaded = ARMul_LoadInstrN (state, pc + (isize * 2), isize); NORMALCYCLE; break; case PCINCEDSEQ: /* Program counter advanced, and an S cycle. */ pc += isize; instr = decoded; decoded = loaded; loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize); NORMALCYCLE; break; case PCINCEDNONSEQ: /* Program counter advanced, and an N cycle. */ pc += isize; instr = decoded; decoded = loaded; loaded = ARMul_LoadInstrN (state, pc + (isize * 2), isize); NORMALCYCLE; break; case RESUME: /* The program counter has been changed. */ pc = state->Reg[15];#ifndef MODE32 pc = pc & R15PCBITS;#endif state->Reg[15] = pc + (isize * 2); state->Aborted = 0; instr = ARMul_ReLoadInstr (state, pc, isize); decoded = ARMul_ReLoadInstr (state, pc + isize, isize); loaded = ARMul_ReLoadInstr (state, pc + isize * 2, isize); NORMALCYCLE; break; default: /* The program counter has been changed. */ pc = state->Reg[15];#ifndef MODE32 pc = pc & R15PCBITS;#endif state->Reg[15] = pc + (isize * 2); state->Aborted = 0; instr = ARMul_LoadInstrN (state, pc, isize); decoded = ARMul_LoadInstrS (state, pc + (isize), isize); loaded = ARMul_LoadInstrS (state, pc + (isize * 2), isize); NORMALCYCLE; break; } if (state->EventSet) ARMul_EnvokeEvent (state);#if 0 /* Enable this for a helpful bit of debugging when tracing is needed. */ fprintf (stderr, "pc: %x, instr: %x\n", pc & ~1, instr); if (instr == 0) abort ();#endif#if 0 /* Enable this code to help track down stack alignment bugs. */ { static ARMword old_sp = -1; if (old_sp != state->Reg[13]) { old_sp = state->Reg[13]; fprintf (stderr, "pc: %08x: SP set to %08x%s\n", pc & ~1, old_sp, (old_sp % 8) ? " [UNALIGNED!]" : ""); } }#endif if (state->Exception) { /* Any exceptions ? */ if (state->NresetSig == LOW) { ARMul_Abort (state, ARMul_ResetV); break; } else if (!state->NfiqSig && !FFLAG) { ARMul_Abort (state, ARMul_FIQV); break; } else if (!state->NirqSig && !IFLAG) { ARMul_Abort (state, ARMul_IRQV); break; } } if (state->CallDebug > 0) { instr = ARMul_Debug (state, pc, instr); if (state->Emulate < ONCE) { state->NextInstr = RESUME; break; } if (state->Debug) { fprintf (stderr, "sim: At %08lx Instr %08lx Mode %02lx\n", pc, instr, state->Mode); (void) fgetc (stdin); } } else if (state->Emulate < ONCE) { state->NextInstr = RESUME; break; } state->NumInstrs++;#ifdef MODET /* Provide Thumb instruction decoding. If the processor is in Thumb mode, then we can simply decode the Thumb instruction, and map it to the corresponding ARM instruction (by directly loading the instr variable, and letting the normal ARM simulator execute). There are some caveats to ensure that the correct pipelined PC value is used when executing Thumb code, and also for dealing with the BL instruction. */ if (TFLAG) { ARMword new; /* Check if in Thumb mode. */ switch (ARMul_ThumbDecode (state, pc, instr, &new)) { case t_undefined: /* This is a Thumb instruction. */ ARMul_UndefInstr (state, instr); goto donext; case t_branch: /* Already processed. */ goto donext; case t_decoded: /* ARM instruction available. */ instr = new; /* So continue instruction decoding. */ break; default: break; } }#endif /* Check the condition codes. */ if ((temp = TOPBITS (28)) == AL) /* Vile deed in the need for speed. */ goto mainswitch; /* Check the condition code. */ switch ((int) TOPBITS (28)) { case AL: temp = TRUE; break; case NV: if (state->is_v5) { if (BITS (25, 27) == 5) /* BLX(1) */ { ARMword dest; state->Reg[14] = pc + 4; /* Force entry into Thumb mode. */ dest = pc + 8 + 1; if (BIT (23)) dest += (NEGBRANCH + (BIT (24) << 1)); else dest += POSBRANCH + (BIT (24) << 1); WriteR15Branch (state, dest); goto donext; } else if ((instr & 0xFC70F000) == 0xF450F000) /* The PLD instruction. Ignored. */ goto donext; else if ( ((instr & 0xfe500f00) == 0xfc100100) || ((instr & 0xfe500f00) == 0xfc000100)) /* wldrw and wstrw are unconditional. */ goto mainswitch; else /* UNDEFINED in v5, UNPREDICTABLE in v3, v4, non executed in v1, v2. */ ARMul_UndefInstr (state, instr); } temp = FALSE; break; case EQ: temp = ZFLAG; break; case NE: temp = !ZFLAG; break; case VS:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -