Landing for Rodolph Perfetta.
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 28 Oct 2010 07:35:07 +0000 (07:35 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 28 Oct 2010 07:35:07 +0000 (07:35 +0000)
Fix the ARM simulator, the ARM disassembler and extend the stop feature.
The stop feature in the simulator now support enabling, disabling and
counting.

BUG=None
TEST=None

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5723 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm/assembler-arm.cc
src/arm/assembler-arm.h
src/arm/constants-arm.h
src/arm/cpu-arm.cc
src/arm/disasm-arm.cc
src/arm/simulator-arm.cc
src/arm/simulator-arm.h

index 7d368bf..ebbd9b1 100644 (file)
@@ -1004,7 +1004,7 @@ void Assembler::blx(int branch_offset) {  // v5 and above
   int h = ((branch_offset & 2) >> 1)*B24;
   int imm24 = branch_offset >> 2;
   ASSERT(is_int24(imm24));
-  emit(15 << 28 | B27 | B25 | h | (imm24 & Imm24Mask));
+  emit(nv | B27 | B25 | h | (imm24 & Imm24Mask));
 }
 
 
@@ -1634,15 +1634,29 @@ void Assembler::stm(BlockAddrMode am,
 
 
 // Exception-generating instructions and debugging support.
-void Assembler::stop(const char* msg) {
+// Stops with a non-negative code less than kNumOfWatchedStops support
+// enabling/disabling and a counter feature. See simulator-arm.h .
+void Assembler::stop(const char* msg, Condition cond, int32_t code) {
 #ifndef __arm__
-  // The simulator handles these special instructions and stops execution.
-  emit(15 << 28 | ((intptr_t) msg));
+  // See constants-arm.h SoftwareInterruptCodes. Unluckily the Assembler and
+  // Simulator do not share constants declaration.
+  ASSERT(code >= kDefaultStopCode);
+  static const uint32_t kStopInterruptCode = 1 << 23;
+  static const uint32_t kMaxStopCode = kStopInterruptCode - 1;
+  // The Simulator will handle the stop instruction and get the message address.
+  // It expects to find the address just after the svc instruction.
+  BlockConstPoolFor(2);
+  if (code >= 0) {
+    svc(kStopInterruptCode + code, cond);
+  } else {
+    svc(kStopInterruptCode + kMaxStopCode, cond);
+  }
+  emit(reinterpret_cast<Instr>(msg));
 #else  // def __arm__
 #ifdef CAN_USE_ARMV5_INSTRUCTIONS
   bkpt(0);
 #else  // ndef CAN_USE_ARMV5_INSTRUCTIONS
-  swi(0x9f0001);
+  svc(0x9f0001);
 #endif  // ndef CAN_USE_ARMV5_INSTRUCTIONS
 #endif  // def __arm__
 }
@@ -1654,7 +1668,7 @@ void Assembler::bkpt(uint32_t imm16) {  // v5 and above
 }
 
 
-void Assembler::swi(uint32_t imm24, Condition cond) {
+void Assembler::svc(uint32_t imm24, Condition cond) {
   ASSERT(is_uint24(imm24));
   emit(cond | 15*B24 | imm24);
 }
index 1c4fd60..5b647a7 100644 (file)
@@ -904,10 +904,13 @@ class Assembler : public Malloced {
   void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al);
 
   // Exception-generating instructions and debugging support
-  void stop(const char* msg);
+  static const int kDefaultStopCode = -1;
+  void stop(const char* msg,
+            Condition cond = al,
+            int32_t code = kDefaultStopCode);
 
   void bkpt(uint32_t imm16);  // v5 and above
-  void swi(uint32_t imm24, Condition cond = al);
+  void svc(uint32_t imm24, Condition cond = al);
 
   // Coprocessor instructions
 
index b2b5cb5..123c5e7 100644 (file)
@@ -186,12 +186,18 @@ enum Shift {
 
 // Special Software Interrupt codes when used in the presence of the ARM
 // simulator.
+// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for
+// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature.
 enum SoftwareInterruptCodes {
   // transition to C code
   call_rt_redirected = 0x10,
   // break point
-  break_point = 0x20
+  break_point = 0x20,
+  // stop
+  stop = 1 << 23
 };
+static const int32_t kStopCodeMask = stop - 1;
+static const uint32_t kMaxStopCode = stop - 1;
 
 
 // Type of VFP register. Determines register encoding.
@@ -325,7 +331,7 @@ class Instr {
   inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); }
 
   // Fields used in Software interrupt instructions
-  inline SoftwareInterruptCodes SwiField() const {
+  inline SoftwareInterruptCodes SvcField() const {
     return static_cast<SoftwareInterruptCodes>(Bits(23, 0));
   }
 
index a3bf483..e998b6f 100644 (file)
@@ -70,7 +70,7 @@ void CPU::FlushICache(void* start, size_t size) {
       // __arm__ may be defined in thumb mode.
       register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
       asm volatile(
-          "swi 0x0"
+          "svc 0x0"
           : "=r" (beg)
           : "0" (beg), "r" (end), "r" (flg), "r" (scno));
     #else
@@ -83,7 +83,7 @@ void CPU::FlushICache(void* start, size_t size) {
           ".ARM            \n"
       "1:  push {r7}       \n\t"
           "mov r7, %4      \n\t"
-          "swi 0x0         \n\t"
+          "svc 0x0         \n\t"
           "pop {r7}        \n\t"
       "@   Enter THUMB Mode\n\t"
           "adr r3, 2f+1    \n\t"
@@ -98,20 +98,20 @@ void CPU::FlushICache(void* start, size_t size) {
     #if defined (__arm__) && !defined(__thumb__)
       // __arm__ may be defined in thumb mode.
       asm volatile(
-          "swi %1"
+          "svc %1"
           : "=r" (beg)
           : "i" (__ARM_NR_cacheflush), "0" (beg), "r" (end), "r" (flg));
     #else
       // Do not use the value of __ARM_NR_cacheflush in the inline assembly
       // below, because the thumb mode value would be used, which would be
-      // wrong, since we switch to ARM mode before executing the swi instruction
+      // wrong, since we switch to ARM mode before executing the svc instruction
       asm volatile(
       "@   Enter ARM Mode  \n\t"
           "adr r3, 1f      \n\t"
           "bx  r3          \n\t"
           ".ALIGN 4        \n\t"
           ".ARM            \n"
-      "1:  swi 0x9f0002    \n"
+      "1:  svc 0x9f0002    \n"
       "@   Enter THUMB Mode\n\t"
           "adr r3, 2f+1    \n\t"
           "bx  r3          \n\t"
index 5122f43..4e7580f 100644 (file)
@@ -108,7 +108,7 @@ class Decoder {
   void PrintShiftImm(Instr* instr);
   void PrintShiftSat(Instr* instr);
   void PrintPU(Instr* instr);
-  void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
+  void PrintSoftwareInterrupt(SoftwareInterruptCodes svc);
 
   // Handle formatting of instructions and their options.
   int FormatRegister(Instr* instr, const char* option);
@@ -126,8 +126,8 @@ class Decoder {
   void DecodeType4(Instr* instr);
   void DecodeType5(Instr* instr);
   void DecodeType6(Instr* instr);
-  void DecodeType7(Instr* instr);
-  void DecodeUnconditional(Instr* instr);
+  // Type 7 includes special Debugger instructions.
+  int DecodeType7(Instr* instr);
   // For VFP support.
   void DecodeTypeVFP(Instr* instr);
   void DecodeType6CoprocessorIns(Instr* instr);
@@ -290,8 +290,8 @@ void Decoder::PrintPU(Instr* instr) {
 
 // Print SoftwareInterrupt codes. Factoring this out reduces the complexity of
 // the FormatOption method.
-void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) {
-  switch (swi) {
+void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) {
+  switch (svc) {
     case call_rt_redirected:
       Print("call_rt_redirected");
       return;
@@ -299,9 +299,16 @@ void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) {
       Print("break_point");
       return;
     default:
-      out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
-                                           "%d",
-                                           swi);
+      if (svc >= stop) {
+        out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+                                             "%d - 0x%x",
+                                             svc & kStopCodeMask,
+                                             svc & kStopCodeMask);
+      } else {
+        out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+                                             "%d",
+                                             svc);
+      }
       return;
   }
 }
@@ -553,9 +560,9 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
           PrintShiftRm(instr);
           return 8;
         }
-      } else if (format[1] == 'w') {  // 'swi
-        ASSERT(STRING_STARTS_WITH(format, "swi"));
-        PrintSoftwareInterrupt(instr->SwiField());
+      } else if (format[1] == 'v') {  // 'svc
+        ASSERT(STRING_STARTS_WITH(format, "svc"));
+        PrintSoftwareInterrupt(instr->SvcField());
         return 3;
       } else if (format[1] == 'i') {  // 'sign: signed extra loads and stores
         ASSERT(STRING_STARTS_WITH(format, "sign"));
@@ -1004,72 +1011,27 @@ void Decoder::DecodeType6(Instr* instr) {
 }
 
 
-void Decoder::DecodeType7(Instr* instr) {
+int Decoder::DecodeType7(Instr* instr) {
   if (instr->Bit(24) == 1) {
-    Format(instr, "swi'cond 'swi");
+    if (instr->SvcField() >= stop) {
+      Format(instr, "stop'cond 'svc");
+      // Also print the stop message. Its address is encoded
+      // in the following 4 bytes.
+      out_buffer_pos_ +=
+        v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+                          "\n  %p  %08x       stop message: %s",
+                          reinterpret_cast<int32_t*>(instr + Instr::kInstrSize),
+                          *reinterpret_cast<char**>(instr + Instr::kInstrSize),
+                          *reinterpret_cast<char**>(instr + Instr::kInstrSize));
+      // We have decoded 2 * Instr::kInstrSize bytes.
+      return 2 * Instr::kInstrSize;
+    } else {
+      Format(instr, "svc'cond 'svc");
+    }
   } else {
     DecodeTypeVFP(instr);
   }
-}
-
-void Decoder::DecodeUnconditional(Instr* instr) {
-  if (instr->Bits(7, 4) == 0xB && instr->Bits(27, 25) == 0 && instr->HasL()) {
-    Format(instr, "'memop'h'pu 'rd, ");
-    bool immediate = instr->HasB();
-    switch (instr->PUField()) {
-      case 0: {
-        // Post index, negative.
-        if (instr->HasW()) {
-          Unknown(instr);
-          break;
-        }
-        if (immediate) {
-          Format(instr, "['rn], #-'imm12");
-        } else {
-          Format(instr, "['rn], -'rm");
-        }
-        break;
-      }
-      case 1: {
-        // Post index, positive.
-        if (instr->HasW()) {
-          Unknown(instr);
-          break;
-        }
-        if (immediate) {
-          Format(instr, "['rn], #+'imm12");
-        } else {
-          Format(instr, "['rn], +'rm");
-        }
-        break;
-      }
-      case 2: {
-        // Pre index or offset, negative.
-        if (immediate) {
-          Format(instr, "['rn, #-'imm12]'w");
-        } else {
-          Format(instr, "['rn, -'rm]'w");
-        }
-        break;
-      }
-      case 3: {
-        // Pre index or offset, positive.
-        if (immediate) {
-          Format(instr, "['rn, #+'imm12]'w");
-        } else {
-          Format(instr, "['rn, +'rm]'w");
-        }
-        break;
-      }
-      default: {
-        // The PU field is a 2-bit field.
-        UNREACHABLE();
-        break;
-      }
-    }
-    return;
-  }
-  Format(instr, "break 'msg");
+  return Instr::kInstrSize;
 }
 
 
@@ -1332,7 +1294,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
                                        "%08x       ",
                                        instr->InstructionBits());
   if (instr->ConditionField() == special_condition) {
-    DecodeUnconditional(instr);
+    UNIMPLEMENTED();
     return Instr::kInstrSize;
   }
   switch (instr->TypeField()) {
@@ -1362,8 +1324,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
       break;
     }
     case 7: {
-      DecodeType7(instr);
-      break;
+      return DecodeType7(instr);
     }
     default: {
       // The type field is 3-bits in the ARM encoding.
index 534e394..cb91520 100644 (file)
@@ -112,15 +112,29 @@ static void InitializeCoverage() {
 
 
 void Debugger::Stop(Instr* instr) {
-  char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff);
-  if (strlen(str) > 0) {
+  // Get the stop code.
+  uint32_t code = instr->SvcField() & kStopCodeMask;
+  // Retrieve the encoded address, which comes just after this stop.
+  char** msg_address =
+    reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
+  char* msg = *msg_address;
+  ASSERT(msg != NULL);
+
+  // Update this stop description.
+  if (isWatchedStop(code) && !watched_stops[code].desc) {
+    watched_stops[code].desc = msg;
+  }
+
+  if (strlen(msg) > 0) {
     if (coverage_log != NULL) {
-      fprintf(coverage_log, "%s\n", str);
+      fprintf(coverage_log, "%s\n", msg);
       fflush(coverage_log);
     }
-    instr->SetInstructionBits(0xe1a00000);  // Overwrite with nop.
+    // Overwrite the instruction and address with nops.
+    instr->SetInstructionBits(kNopInstr);
+    reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr);
   }
-  sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+  sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize);
 }
 
 #else  // ndef GENERATED_CODE_COVERAGE
@@ -130,9 +144,16 @@ static void InitializeCoverage() {
 
 
 void Debugger::Stop(Instr* instr) {
-  const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff);
-  PrintF("Simulator hit %s\n", str);
-  sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+  // Get the stop code.
+  uint32_t code = instr->SvcField() & kStopCodeMask;
+  // Retrieve the encoded address, which comes just after this stop.
+  char* msg = *reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
+  // Update this stop description.
+  if (sim_->isWatchedStop(code) && !sim_->watched_stops[code].desc) {
+    sim_->watched_stops[code].desc = msg;
+  }
+  PrintF("Simulator hit %s\n", msg);
+  sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize);
   Debug();
 }
 #endif
@@ -359,6 +380,7 @@ void Debugger::Debug() {
         // use a reasonably large buffer
         v8::internal::EmbeddedVector<char, 256> buffer;
 
+        byte* prev = NULL;
         byte* cur = NULL;
         byte* end = NULL;
 
@@ -368,9 +390,9 @@ void Debugger::Debug() {
         } else if (argc == 2) {
           int32_t value;
           if (GetValue(arg1, &value)) {
-            cur = reinterpret_cast<byte*>(value);
-            // no length parameter passed, assume 10 instructions
-            end = cur + (10 * Instr::kInstrSize);
+            cur = reinterpret_cast<byte*>(sim_->get_pc());
+            // Disassemble <arg1> instructions.
+            end = cur + (value * Instr::kInstrSize);
           }
         } else {
           int32_t value1;
@@ -382,10 +404,10 @@ void Debugger::Debug() {
         }
 
         while (cur < end) {
-          dasm.InstructionDecode(buffer, cur);
+          prev = cur;
+          cur += dasm.InstructionDecode(buffer, cur);
           PrintF("  0x%08x  %s\n",
-                 reinterpret_cast<intptr_t>(cur), buffer.start());
-          cur += Instr::kInstrSize;
+                 reinterpret_cast<intptr_t>(prev), buffer.start());
         }
       } else if (strcmp(cmd, "gdb") == 0) {
         PrintF("relinquishing control to gdb\n");
@@ -418,13 +440,58 @@ void Debugger::Debug() {
         PrintF("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_);
         PrintF("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_);
         PrintF("INEXACT flag: %d; ", sim_->inexact_vfp_flag_);
-      } else if (strcmp(cmd, "unstop") == 0) {
-        intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize;
+      } else if (strcmp(cmd, "stop") == 0) {
+        int32_t value;
+        intptr_t stop_pc = sim_->get_pc() - 2 * Instr::kInstrSize;
         Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc);
-        if (stop_instr->ConditionField() == special_condition) {
-          stop_instr->SetInstructionBits(kNopInstr);
+        Instr* msg_address =
+          reinterpret_cast<Instr*>(stop_pc + Instr::kInstrSize);
+        if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
+          // Remove the current stop.
+          if (sim_->isStopInstruction(stop_instr)) {
+            stop_instr->SetInstructionBits(kNopInstr);
+            msg_address->SetInstructionBits(kNopInstr);
+          } else {
+            PrintF("Not at debugger stop.\n");
+          }
+        } else if (argc == 3) {
+          // Print information about all/the specified breakpoint(s).
+          if (strcmp(arg1, "info") == 0) {
+            if (strcmp(arg2, "all") == 0) {
+              PrintF("Stop information:\n");
+              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+                sim_->PrintStopInfo(i);
+              }
+            } else if (GetValue(arg2, &value)) {
+              sim_->PrintStopInfo(value);
+            } else {
+              PrintF("Unrecognized argument.\n");
+            }
+          } else if (strcmp(arg1, "enable") == 0) {
+            // Enable all/the specified breakpoint(s).
+            if (strcmp(arg2, "all") == 0) {
+              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+                sim_->EnableStop(i);
+              }
+            } else if (GetValue(arg2, &value)) {
+              sim_->EnableStop(value);
+            } else {
+              PrintF("Unrecognized argument.\n");
+            }
+          } else if (strcmp(arg1, "disable") == 0) {
+            // Disable all/the specified breakpoint(s).
+            if (strcmp(arg2, "all") == 0) {
+              for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+                sim_->DisableStop(i);
+              }
+            } else if (GetValue(arg2, &value)) {
+              sim_->DisableStop(value);
+            } else {
+              PrintF("Unrecognized argument.\n");
+            }
+          }
         } else {
-          PrintF("Not at debugger stop.");
+          PrintF("Wrong usage. Use help command for more information.\n");
         }
       } else if ((strcmp(cmd, "t") == 0) || strcmp(cmd, "trace") == 0) {
         ::v8::internal::FLAG_trace_sim = !::v8::internal::FLAG_trace_sim;
@@ -455,11 +522,29 @@ void Debugger::Debug() {
         PrintF("  set a break point on the address\n");
         PrintF("del\n");
         PrintF("  delete the breakpoint\n");
-        PrintF("unstop\n");
-        PrintF("  ignore the stop instruction at the current location");
-        PrintF("  from now on\n");
         PrintF("trace (alias 't')\n");
         PrintF("  toogle the tracing of all executed statements\n");
+        PrintF("stop feature:\n");
+        PrintF("  Description:\n");
+        PrintF("    Stops are debug instructions inserted by\n");
+        PrintF("    the Assembler::stop() function.\n");
+        PrintF("    When hitting a stop, the Simulator will\n");
+        PrintF("    stop and and give control to the Debugger.\n");
+        PrintF("    The first %d stop codes are watched:\n",
+               Simulator::kNumOfWatchedStops);
+        PrintF("    - They can be enabled / disabled: the Simulator\n");
+        PrintF("       will / won't stop when hitting them.\n");
+        PrintF("    - The Simulator keeps track of how many times they \n");
+        PrintF("      are met. (See the info command.) Going over a\n");
+        PrintF("      disabled stop still increases its counter. \n");
+        PrintF("  Commands:\n");
+        PrintF("    stop info all/<code> : print infos about number <code>\n");
+        PrintF("      or all stop(s).\n");
+        PrintF("    stop enable/disable all/<code> : enables / disables\n");
+        PrintF("      all or number <code> stop(s)\n");
+        PrintF("    stop unstop\n");
+        PrintF("      ignore the stop instruction at the current location\n");
+        PrintF("      from now on\n");
       } else {
         PrintF("Unknown command: %s\n", cmd);
       }
@@ -643,9 +728,9 @@ Simulator::Simulator() {
 // the simulator.  The external reference will be a function compiled for the
 // host architecture.  We need to call that function instead of trying to
 // execute it with the simulator.  We do that by redirecting the external
-// reference to a swi (software-interrupt) instruction that is handled by
+// reference to a svc (Supervisor Call) instruction that is handled by
 // the simulator.  We write the original destination of the jump just at a known
-// offset from the swi instruction so the simulator knows what to call.
+// offset from the svc instruction so the simulator knows what to call.
 class Redirection {
  public:
   Redirection(void* external_function, bool fp_return)
@@ -1434,8 +1519,8 @@ typedef double (*SimulatorRuntimeFPCall)(int32_t arg0,
 // Software interrupt instructions are used by the simulator to call into the
 // C-based V8 runtime.
 void Simulator::SoftwareInterrupt(Instr* instr) {
-  int swi = instr->SwiField();
-  switch (swi) {
+  int svc = instr->SvcField();
+  switch (svc) {
     case call_rt_redirected: {
       // Check if stack is aligned. Error if not aligned is reported below to
       // include information on the function called.
@@ -1505,9 +1590,98 @@ void Simulator::SoftwareInterrupt(Instr* instr) {
       dbg.Debug();
       break;
     }
+    // stop uses all codes greater than 1 << 23.
     default: {
-      UNREACHABLE();
-      break;
+      if (svc >= (1 << 23)) {
+        uint32_t code = svc & kStopCodeMask;
+        if (isWatchedStop(code)) {
+          IncreaseStopCounter(code);
+        }
+        // Stop if it is enabled, otherwise go on jumping over the stop
+        // and the message address.
+        if (isEnabledStop(code)) {
+          Debugger dbg(this);
+          dbg.Stop(instr);
+        } else {
+          set_pc(get_pc() + 2 * Instr::kInstrSize);
+        }
+      } else {
+        // This is not a valid svc code.
+        UNREACHABLE();
+        break;
+      }
+    }
+  }
+}
+
+
+// Stop helper functions.
+bool Simulator::isStopInstruction(Instr* instr) {
+  return (instr->Bits(27, 24) == 0xF) && (instr->SvcField() >= stop);
+}
+
+
+bool Simulator::isWatchedStop(uint32_t code) {
+  ASSERT(code <= kMaxStopCode);
+  return code < kNumOfWatchedStops;
+}
+
+
+bool Simulator::isEnabledStop(uint32_t code) {
+  ASSERT(code <= kMaxStopCode);
+  // Unwatched stops are always enabled.
+  return !isWatchedStop(code) ||
+    !(watched_stops[code].count & kStopDisabledBit);
+}
+
+
+void Simulator::EnableStop(uint32_t code) {
+  ASSERT(isWatchedStop(code));
+  if (!isEnabledStop(code)) {
+    watched_stops[code].count &= ~kStopDisabledBit;
+  }
+}
+
+
+void Simulator::DisableStop(uint32_t code) {
+  ASSERT(isWatchedStop(code));
+  if (isEnabledStop(code)) {
+    watched_stops[code].count |= kStopDisabledBit;
+  }
+}
+
+
+void Simulator::IncreaseStopCounter(uint32_t code) {
+  ASSERT(code <= kMaxStopCode);
+  ASSERT(isWatchedStop(code));
+  if ((watched_stops[code].count & ~(1 << 31)) == 0x7fffffff) {
+    PrintF("Stop counter for code %i has overflowed.\n"
+           "Enabling this code and reseting the counter to 0.\n", code);
+    watched_stops[code].count = 0;
+    EnableStop(code);
+  } else {
+    watched_stops[code].count++;
+  }
+}
+
+
+// Print a stop status.
+void Simulator::PrintStopInfo(uint32_t code) {
+  ASSERT(code <= kMaxStopCode);
+  if (!isWatchedStop(code)) {
+    PrintF("Stop not watched.");
+  } else {
+    const char* state = isEnabledStop(code) ? "Enabled" : "Disabled";
+    int32_t count = watched_stops[code].count & ~kStopDisabledBit;
+    // Don't print the state of unused breakpoints.
+    if (count != 0) {
+      if (watched_stops[code].desc) {
+        PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
+               code, code, state, count, watched_stops[code].desc);
+      } else {
+        PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
+               code, code, state, count);
+      }
     }
   }
 }
@@ -2216,73 +2390,6 @@ void Simulator::DecodeType7(Instr* instr) {
 }
 
 
-void Simulator::DecodeUnconditional(Instr* instr) {
-  if (instr->Bits(7, 4) == 0x0B && instr->Bits(27, 25) == 0 && instr->HasL()) {
-    // Load halfword instruction, either register or immediate offset.
-    int rd = instr->RdField();
-    int rn = instr->RnField();
-    int32_t rn_val = get_register(rn);
-    int32_t addr = 0;
-    int32_t offset;
-    if (instr->Bit(22) == 0) {
-      // Register offset.
-      int rm = instr->RmField();
-      offset = get_register(rm);
-    } else {
-      // Immediate offset
-      offset = instr->Bits(3, 0) + (instr->Bits(11, 8) << 4);
-    }
-    switch (instr->PUField()) {
-      case 0: {
-        // Post index, negative.
-        ASSERT(!instr->HasW());
-        addr = rn_val;
-        rn_val -= offset;
-        set_register(rn, rn_val);
-        break;
-      }
-      case 1: {
-        // Post index, positive.
-        ASSERT(!instr->HasW());
-        addr = rn_val;
-        rn_val += offset;
-        set_register(rn, rn_val);
-        break;
-      }
-      case 2: {
-        // Pre index or offset, negative.
-        rn_val -= offset;
-        addr = rn_val;
-        if (instr->HasW()) {
-          set_register(rn, rn_val);
-        }
-        break;
-      }
-      case 3: {
-        // Pre index or offset, positive.
-        rn_val += offset;
-        addr = rn_val;
-        if (instr->HasW()) {
-          set_register(rn, rn_val);
-        }
-        break;
-      }
-      default: {
-        // The PU field is a 2-bit field.
-        UNREACHABLE();
-        break;
-      }
-    }
-    // Not sign extending, so load as unsigned.
-    uint16_t halfword = ReadH(addr, instr);
-    set_register(rd, halfword);
-  } else {
-    Debugger dbg(this);
-    dbg.Stop(instr);
-  }
-}
-
-
 // void Simulator::DecodeTypeVFP(Instr* instr)
 // The Following ARMv7 VFPv instructions are currently supported.
 // vmov :Sn = Rt
@@ -2655,7 +2762,7 @@ void Simulator::InstructionDecode(Instr* instr) {
     PrintF("  0x%08x  %s\n", reinterpret_cast<intptr_t>(instr), buffer.start());
   }
   if (instr->ConditionField() == special_condition) {
-    DecodeUnconditional(instr);
+    UNIMPLEMENTED();
   } else if (ConditionallyExecute(instr)) {
     switch (instr->TypeField()) {
       case 0:
index e0658fc..3e02348 100644 (file)
@@ -226,6 +226,15 @@ class Simulator {
   void HandleRList(Instr* instr, bool load);
   void SoftwareInterrupt(Instr* instr);
 
+  // Stop helper functions.
+  inline bool isStopInstruction(Instr* instr);
+  inline bool isWatchedStop(uint32_t bkpt_code);
+  inline bool isEnabledStop(uint32_t bkpt_code);
+  inline void EnableStop(uint32_t bkpt_code);
+  inline void DisableStop(uint32_t bkpt_code);
+  inline void IncreaseStopCounter(uint32_t bkpt_code);
+  void PrintStopInfo(uint32_t code);
+
   // Read and write memory.
   inline uint8_t ReadBU(int32_t addr);
   inline int8_t ReadB(int32_t addr);
@@ -252,7 +261,6 @@ class Simulator {
   void DecodeType5(Instr* instr);
   void DecodeType6(Instr* instr);
   void DecodeType7(Instr* instr);
-  void DecodeUnconditional(Instr* instr);
 
   // Support for VFP.
   void DecodeTypeVFP(Instr* instr);
@@ -317,6 +325,23 @@ class Simulator {
   // Registered breakpoints.
   Instr* break_pc_;
   instr_t break_instr_;
+
+  // A stop is watched if its code is less than kNumOfWatchedStops.
+  // Only watched stops support enabling/disabling and the counter feature.
+  static const uint32_t kNumOfWatchedStops = 256;
+
+  // Breakpoint is disabled if bit 31 is set.
+  static const uint32_t kStopDisabledBit = 1 << 31;
+
+  // A stop is enabled, meaning the simulator will stop when meeting the
+  // instruction, if bit 31 of watched_stops[code].count is unset.
+  // The value watched_stops[code].count & ~(1 << 31) indicates how many times
+  // the breakpoint was hit or gone through.
+  struct StopCoundAndDesc {
+    uint32_t count;
+    char* desc;
+  };
+  StopCoundAndDesc watched_stops[kNumOfWatchedStops];
 };
 
 } }  // namespace assembler::arm