diff packages/hal/arm/arch/current/src/arm_stub.c @ 76:435cced73e2f ecos-v1_3_1-release

eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
author jlarmour
date Tue, 28 Mar 2000 14:10:45 +0000
parents da3908c9cd10
children 59d97b6ba612
line wrap: on
line diff
--- a/packages/hal/arm/arch/current/src/arm_stub.c
+++ b/packages/hal/arm/arch/current/src/arm_stub.c
@@ -64,6 +64,13 @@
 #define TRUE  1
 #endif
 
+// Use bit 0 as a thumb-mode flag for next address to be executed.
+// Alternative would be to keep track of it using a C variable, but
+// since bit 0 is used by the BX instruction, we might as well do the
+// same thing and thus avoid checking two different flags.
+#define IS_THUMB_ADDR(addr)     ((addr) & 1)
+#define MAKE_THUMB_ADDR(addr) ((addr) | 1)
+#define UNMAKE_THUMB_ADDR(addr) ((addr) & ~1)
 
 #ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
 #include <cyg/hal/dbg-threads-api.h>    // dbg_currthread_id
@@ -97,8 +104,7 @@ int __get_trap_number (void)
     return _hal_registers->vector;
 }
 
-/* Set the currently-saved pc register value to PC. This also updates NPC
-   as needed. */
+/* Set the currently-saved pc register value to PC. */
 
 void set_pc (target_register_t pc)
 {
@@ -116,6 +122,7 @@ void set_pc (target_register_t pc)
 
 static unsigned long  ss_saved_pc = 0;
 static unsigned long  ss_saved_instr;
+static unsigned short ss_saved_thumb_instr;
 
 #define FIXME() {diag_printf("FIXME - %s\n", __FUNCTION__); }
 
@@ -225,6 +232,11 @@ target_ins(unsigned long *pc, unsigned l
     int i, reg_count, c;
     switch ((ins & 0x0C000000) >> 26) {
     case 0x0:
+        // BX
+        if ((ins & 0x0FFFFFF0) == 0x012FFF10) {
+            new_pc = (unsigned long)get_register(ins & 0x0000000F);
+            return ((unsigned long *)new_pc);
+        }
         // Data processing
         new_pc = (unsigned long)(pc+1);
         if ((ins & 0x0000F000) == 0x0000F000) {
@@ -376,10 +388,67 @@ target_ins(unsigned long *pc, unsigned l
     }
 }
 
+// FIXME: target_ins also needs to check for CPSR/THUMB being set and
+//        set the thumb bit accordingly.
+
+static unsigned long
+target_thumb_ins(unsigned long pc, unsigned short ins)
+{
+    unsigned long new_pc = MAKE_THUMB_ADDR(pc+2); // default is fall-through 
+                                        // to next thumb instruction
+    unsigned long offset, arm_ins;
+
+    switch ((ins & 0xf000) >> 12) {
+    case 0x4:
+        // Check for BX
+        if ((ins & 0xff87) == 0x4700)
+            new_pc = (unsigned long)get_register((ins & 0x00078) >> 3);
+        break;
+    case 0xd:
+        // Bcc
+        // Use ARM function to check condition
+        arm_ins = ((unsigned long)(ins & 0x0f00)) << 20;
+        if (ins_will_execute(arm_ins)) {
+            offset = (ins & 0x00FF) << 1;
+            if (ins & 0x0080) offset |= 0xFFFFFE00;  // sign extend
+            new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset);
+        }
+        break;
+    case 0xe:
+        // check for B
+        if ((ins & 0x0800) == 0) {
+            offset = (ins & 0x07FF) << 1;
+            if (ins & 0x0400) offset |= 0xFFFFF800;  // sign extend
+            new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset);
+        }
+        break;
+    case 0xf:
+        // BL (4byte instruction!)
+        // First instruction (bit 11 == 0) holds top-part of offset
+        offset = (ins & 0x07FF) << 12;
+        if (ins & 0x0400) offset |= 0xFF800000;  // sign extend
+        // Get second instruction
+        // Second instruction (bit 11 == 1) holds bottom-part of offset
+        ins = *(unsigned short*)(pc+2);
+        offset |= (ins & 0x07ff) << 1;
+        new_pc = MAKE_THUMB_ADDR((unsigned long)(pc+4) + offset);
+        break;
+    }
+
+    return new_pc;
+}
 
 void __single_step (void)
 {
     unsigned long pc = get_register(PC);
+    unsigned long cpsr = get_register(PS);
+
+    // Calculate address of next instruction to be executed
+    if (cpsr & CPSR_THUMB_ENABLE) {
+        // thumb
+        ss_saved_pc = target_thumb_ins(pc, *(unsigned short*)pc);
+    } else {
+        // ARM
         unsigned long curins = *(unsigned long*)pc;
         if (ins_will_execute(curins)) {
             // Decode instruction to decide what the next PC will be
@@ -390,8 +459,19 @@ void __single_step (void)
             // don't hold)
             ss_saved_pc = pc+4;
         }
+    }
+
+    // Set breakpoint according to type
+    if (IS_THUMB_ADDR(ss_saved_pc)) {
+        // Thumb instruction
+        unsigned long t_pc = UNMAKE_THUMB_ADDR(ss_saved_pc);
+        ss_saved_thumb_instr = *(unsigned short*)t_pc;
+        *(unsigned short*)t_pc = HAL_BREAKINST_THUMB;
+    } else {
+        // ARM instruction
         ss_saved_instr = *(unsigned long*)ss_saved_pc;
         *(unsigned long*)ss_saved_pc = HAL_BREAKINST_ARM;
+    }
 }
 
 /* Clear the single-step state. */
@@ -399,12 +479,20 @@ void __single_step (void)
 void __clear_single_step (void)
 {
     if (ss_saved_pc != 0) {
+        // Restore instruction according to type
+        if (IS_THUMB_ADDR(ss_saved_pc)) {
+            // Thumb instruction
+            unsigned long t_pc = UNMAKE_THUMB_ADDR(ss_saved_pc);
+            *(unsigned short*)t_pc = ss_saved_thumb_instr;
+        } else {
             // ARM instruction
             *(unsigned long*)ss_saved_pc = ss_saved_instr;
+        }
         ss_saved_pc = 0;
     }
 }
 
+#if !defined(CYGPKG_CYGMON)
 void __install_breakpoints (void)
 {
 //    FIXME();
@@ -414,6 +502,7 @@ void __clear_breakpoints (void)
 {
 //    FIXME();
 }
+#endif // !CYGPKG_CYGMON
 
 /* If the breakpoint we hit is in the breakpoint() instruction, return a
    non-zero value. */
@@ -432,11 +521,109 @@ int
 void __skipinst (void)
 {
     unsigned long pc = get_register(PC);
+    unsigned long cpsr = get_register(PS);
+
+    if (cpsr & CPSR_THUMB_ENABLE)
+        pc += 2;
+    else
         pc += 4;
 
     put_register(PC, pc);
 }
 
+//-----------------------------------------------------------------------
+// Thumb-aware GDB interrupt handler.
+// This is a brute-force replacement of the ones in hal_stub.c. Need to
+// find a better way of handling it... Maybe... Probably only ARM/thumb
+// that is this weird.
+
+typedef struct
+{
+    cyg_uint32 targetAddr;
+    union {
+        cyg_uint32 arm_instr;
+        cyg_uint16 thumb_instr;
+    } savedInstr;
+} instrBuffer;
+
+static instrBuffer break_buffer;
+
+volatile int cyg_hal_gdb_running_step = 0;
+
+void 
+cyg_hal_gdb_interrupt (target_register_t pc)
+{
+    // Clear flag that we Continued instead of Stepping
+    cyg_hal_gdb_running_step = 0;
+    // and override existing break? So that a ^C takes effect...
+    if (0 != break_buffer.targetAddr)
+        cyg_hal_gdb_remove_break( break_buffer.targetAddr );
+
+    if (0 == break_buffer.targetAddr) {
+        cyg_uint32 cpsr = get_register(PS);
+
+        if (cpsr & CPSR_THUMB_ENABLE) {
+            break_buffer.targetAddr = MAKE_THUMB_ADDR((cyg_uint32)pc);
+            break_buffer.savedInstr.thumb_instr = *(cyg_uint16*)pc;
+            *(cyg_uint16*)pc = HAL_BREAKINST_THUMB;
+        } else {
+            break_buffer.targetAddr = (cyg_uint32)pc;
+            break_buffer.savedInstr.arm_instr = *(cyg_uint32*)pc;
+            *(cyg_uint32*)pc = HAL_BREAKINST_ARM;
+        }
+
+        __data_cache(CACHE_FLUSH);
+        __instruction_cache(CACHE_FLUSH);
+    }
+}
+
+void 
+cyg_hal_gdb_place_break (target_register_t pc)
+{
+    // Clear flag that we Continued instead of Stepping
+    cyg_hal_gdb_running_step = 0;
+    // Unconditionally place an ARM breakpoint not a THUMB; there is
+    // no saved regset for get_register in this call:
+    if (0 == break_buffer.targetAddr) {
+        break_buffer.targetAddr = (cyg_uint32)pc;
+        break_buffer.savedInstr.arm_instr = *(cyg_uint32*)pc;
+        *(cyg_uint32*)pc = HAL_BREAKINST_ARM;
+        
+        __data_cache(CACHE_FLUSH);
+        __instruction_cache(CACHE_FLUSH);
+    }
+}
+
+int 
+cyg_hal_gdb_remove_break (target_register_t pc)
+{
+    if ( cyg_hal_gdb_running_step )
+        return 0; // Do not remove the break: we must hit it!
+
+    if (pc == UNMAKE_THUMB_ADDR(break_buffer.targetAddr)) {
+        if (IS_THUMB_ADDR(break_buffer.targetAddr)) {
+            *(cyg_uint16*)pc = break_buffer.savedInstr.thumb_instr;
+        } else {
+            *(cyg_uint32*)pc = break_buffer.savedInstr.arm_instr;
+        }
+        break_buffer.targetAddr = 0;
+
+        __data_cache(CACHE_FLUSH);
+        __instruction_cache(CACHE_FLUSH);
+        return 1;
+    }
+    return 0;
+}
+
+int
+cyg_hal_gdb_break_is_set (void)
+{
+    if (0 != break_buffer.targetAddr) {
+        return 1;
+    }
+    return 0;
+}
+
 
 
 #endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS