summaryrefslogtreecommitdiff
path: root/AT91SAM7S256/armdebug/Debugger/debug_stub.S
diff options
context:
space:
mode:
Diffstat (limited to 'AT91SAM7S256/armdebug/Debugger/debug_stub.S')
-rw-r--r--AT91SAM7S256/armdebug/Debugger/debug_stub.S2128
1 files changed, 2128 insertions, 0 deletions
diff --git a/AT91SAM7S256/armdebug/Debugger/debug_stub.S b/AT91SAM7S256/armdebug/Debugger/debug_stub.S
new file mode 100644
index 0000000..55b2183
--- /dev/null
+++ b/AT91SAM7S256/armdebug/Debugger/debug_stub.S
@@ -0,0 +1,2128 @@
+/** @file debug_stub.S
+ * @brief ARM Breakpoint Debugger support routines
+ *
+ */
+
+/* Copyright (C) 2007-2011 the NxOS developers
+ *
+ * Module Developed by: TC Wan <tcwan@cs.usm.my>
+ *
+ * See AUTHORS for a full list of the developers.
+ *
+ * See COPYING for redistribution license
+ *
+ */
+
+ /* GDB sparc-stub.c comments header included below to document GDB Server Remote protocol */
+ /* This header has been modified to include additional commands not documented in the header stub */
+
+ /****************************************************************************
+
+ THIS SOFTWARE IS NOT COPYRIGHTED
+
+ HP offers the following for use in the public domain. HP makes no
+ warranty with regard to the software or it's performance and the
+ user accepts the software "AS IS" with all faults.
+
+ HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
+ TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for SPARC by Stu Grossman, Cygnus Support.
+ *
+ * This code has been extensively tested on the Fujitsu SPARClite demo board.
+ *
+ * To enable debugger support, two things need to happen. One, a
+ * call to set_debug_traps() is necessary in order to allow any breakpoints
+ * or error conditions to be properly intercepted and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication. This
+ * is most easily accomplished by a call to breakpoint(). Breakpoint()
+ * simulates a breakpoint by executing a trap #1.
+ *
+ *************
+ *
+ * The following gdb commands are supported:
+ *
+ * command function Return value
+ *
+ * g return the value of the CPU registers hex data or ENN
+ * GrrrrRRRR.. set the value of the CPU registers OK or ENN
+ * where register values are given as
+ * 32-bit hex values in the sequence:
+ * User R0, R1, ..., R15, CPSR
+ * px get the value of one register (x) hex data or ENN
+ * Px=rrrr set the value of one register (x) to OK or ENN
+ * 32-bit hex value rrrr.
+ * x = ['0','F'] for R0-R15, '!' for User CPSR
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL:bb..bb
+ * Write LLLL bytes at address AA.AA OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * zt,AA..AA,k Remove a Breakpoint of type t at addr OK or ENN
+ * AA..AA of kind k
+ * Zt,AA..AA,k Insert a Breakpoint of type t at addr OK or ENN
+ * AA..AA of kind k
+ * t 0: memory breakpoint
+ * 1: hardware breakpoint
+ * 2: write watchpoint
+ * 3: read watchpoint
+ * 4: access watchpoint
+ * k: 2 (16-bit Thumb), 3 (32-bit Thumb2)
+ * or 4 (32-bit ARM) for t=[0,1]
+ * Num. bytes to watch for t=[2,4]
+ *
+ * All commands and responses are sent with a packet which includes a
+ * checksum. A packet consists of
+ *
+ * $<packet info>#<checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+ /* Modified GDB Server Remote Protocol definition from GDB's sparc-stub.c Comment Header included above
+ * Additional commands from GDB Reference Appendix D.2
+ *
+ * Note: ARMDEBUG can only implement Memory Breakpoints t=0. Hardware breakpoints requires a JTAG debugger.
+ * Currently, watchpoints are not implemented as they require hardware support as well (need verification).
+ *
+ * GDB requires command parameters to be specified as Big Endian values.
+ * However, the read/write register command expect the register contents to be specified in target byte order.
+ * The default target byte order is Little Endian for the AT91SAM7xxx processor in the NXT.
+ * If Big Endian target byte order is required, the __BIG_ENDIAN__ preprocessor label should be defined.
+ */
+
+/* FIXME: The Hex value arguments passed by GDB does not have fixed lengths! Although the standard says
+ * there should be x digits, it does not follow this requirement. e.g., register index.
+ */
+
+
+#define __ASSEMBLY__
+#include "debug_stub.h"
+#include "debug_macros.h"
+
+/* Macro definitions */
+
+/* _check_msgseparator
+ * Look for separator ','
+ * On entry:
+ * bufferptr: points to the parameter buffer [can't be R0]
+ * On exit:
+ * R0: destroyed
+ * bufferptr: points to the next character location in the parameter buffer
+ * Flags: Updated
+ */
+
+ .macro _check_msgseparator bufferptr
+ ldrb r0, [\bufferptr], #1 /* get separator */
+ cmp r0, #MSGBUF_SEPCHAR
+ .endm
+
+/* _check_msgargument
+ * Look for argument ':'
+ * On entry:
+ * bufferptr: points to the parameter buffer [can't be R0]
+ * On exit:
+ * R0: destroyed
+ * bufferptr: points to the next character location in the parameter buffer
+ * Flags: Updated
+ */
+
+ .macro _check_msgargument bufferptr
+ ldrb r0, [\bufferptr], #1 /* get separator */
+ cmp r0, #MSGBUF_ARGCHAR
+ .endm
+
+/* _check_msgassignment
+ * Look for assignment '='
+ * On entry:
+ * bufferptr: points to the parameter buffer [can't be R0]
+ * On exit:
+ * R0: destroyed
+ * bufferptr: points to the next character location in the parameter buffer
+ * Flags: Updated
+ */
+
+ .macro _check_msgassignment bufferptr
+ ldrb r0, [\bufferptr], #1 /* get separator */
+ cmp r0, #MSGBUF_SETCHAR
+ .endm
+
+.bss
+.align 4
+debug_curr_breakpoint:
+ .word 0x0
+debug_InMsgBuf:
+ .space MSGBUF_SIZE,0
+debug_OutMsgBuf:
+ .space MSGBUF_SIZE,0
+
+debug_state:
+ .byte 0x0
+debug_mode:
+ .byte 0x0 /* Boolean variable */
+
+.data
+.align 4
+debug_RetransmitFlag:
+ .byte '-',0
+
+debug_ValidResponsePrefix:
+ .byte '+','$',0
+
+debug_ErrorResponsePrefix:
+ .byte '+','$','E',0
+
+debug_SignalResponsePrefix:
+ .byte '+','$','S',0
+
+debug_OkResponse:
+ .byte '+','$','O','K',0
+
+/* The CmdIndexTable and CmdJumpTable must be kept in sync */
+debug_cmdIndexTable:
+ .byte 'g','G','p','P','m','M','c','s','k','z','Z','?','q','Q',0
+
+/* Command Handlers
+ * On entry:
+ * R0: Input Message Parameter Buffer address pointer (points to contents after '$' and '<cmdchar>')
+ */
+.align 4
+debug_cmdJumpTable:
+ .word _dbg__cmd_GetAllRegs /* 'g' */
+ .word _dbg__cmd_SetAllRegs /* 'G' */
+ .word _dbg__cmd_GetOneReg /* 'p' */
+ .word _dbg__cmd_SetOneReg /* 'P' */
+ .word _dbg__cmd_ReadMem /* 'm' */
+ .word _dbg__cmd_WriteMem /* 'M' */
+ .word _dbg__cmd_Continue /* 'c' */
+ .word _dbg__nop /* 's' */
+ .word _dbg__nop /* 'k' */
+ .word _dbg__cmd_remove_breakpoint /* 'z' */
+ .word _dbg__cmd_insert_breakpoint /* 'Z' */
+ .word _dbg__cmd_Status /* '?' */
+ .word _dbg__cmd_Query /* 'q' */
+ .word _dbg__nop /* 'Q' */
+ .word 0
+
+/*
+ * To determine the next instruction to execute, we need to check current (breakpointed) instruction
+ * and determine whether it will be executed or not. This necessitates a mini instruction decoder
+ * that can check the type of instruction, as well as if it'll affect the PC.
+ * The instruction decoder used here is table based. Each entry in the table consists of:
+ * Instruction Identifier (IID), Instruction Bitmask (IBM), Instruction Handler Address (IHA)
+ * Null entries are placed at the end of the table.
+ *
+ * This allows for a flexible approach to handling instructions that we're interested in, at the expense
+ * of memory usage.
+ *
+ * For ARM, the IID & IBM are both 4 bytes, whereas the Thumb IID & IBM are 2 bytes.
+ * The IHA is always 4 bytes.
+ */
+
+/* ARM Instruction Decode Table
+ * .word IID, IBM, IHA (12 bytes)
+ */
+
+debug_armDecodeTable:
+ .word 0x0000f000, 0x0c00f000, _arm_data_instr_handler /* Data Processing instr with Rd = R15 */
+ .word 0x012fff10, 0x0ffffff0, _arm_bx_blx_handler /* BX or BLX */
+ .word 0x0410f000, 0x0410f000, _arm_ldr_pc_handler /* LDR with Rd = PC */
+/* .word 0x06000010, 0x0e000010, _arm_undef_handler */ /* Undefined instr: shouldn't occur, as it would've been trapped already. See _dbg_next_instruction_addr */
+ .word 0x08108000, 0x0e108000, _arm_ldm_pc_handler /* LDM {pc} */
+ .word 0x0a000000, 0x0e000000, _arm_b_bl_handler /* B or BL. Note v4t does not have BLX instr */
+ .word 0x0c000000, 0x0c000000, _arm_coproc_swi_handler /* Coprocessor instr or SWI */
+ .word 0x0,0x0,0x0 /* Null Entry */
+
+/* Thumb Instruction Decode Table
+ * .hword IID, IBM
+ * .word IHA (8 bytes)
+ */
+
+debug_thumbDecodeTable:
+ .hword 0x4700, 0xff07
+ .word _thumb_bx_blx_handler /* BX or BLX. Note: b7 (H1) is not matched in the mask */
+ .hword 0xbd00, 0xff00
+ .word _thumb_poppc_handler /* PUSH/POP, specifically POP {Rlist,PC} */
+ .hword 0xd000, 0xf000
+ .word _thumb_bcond_swi_handler /* B<cond> or SWI */
+ .hword 0xe000, 0xf800
+ .word _thumb_b_handler /* B */
+ .hword 0xf000, 0xf000
+ .word _thumb_long_b_handler /* Long BL or BLX (4 bytes) Note: b11 (H) indicates 1st or 2nd instr */
+ .hword 0x0,0x0
+ .word 0x0 /* Null Entry */
+
+/* ARM Condition Code Mapping Table
+ * Converts Instruction encoding to SPSR Flags.
+ * b31 b30 b29 b28
+ * N Z C V
+ * Indexed according to Instruction Encoding order (pg 30, Table 6, ATMEL ARM7TDMI Data Sheet)
+ * Condition Code stored in MSN(set), LSN(clr) order
+ * Note1: 0x00 = AL. NV is deprecated, treat as AL
+ * Note2: 0xFF indicates that the condition checks needs to be handled separately (complex checks)
+ *
+ * EQ: Z set
+ * NE: Z clr
+ * HS/CS: C set
+ * LO/CC: C clr
+ * MI: N set
+ * PL: N clr
+ * VS: V set
+ * VC: V clr
+ * HI: C set AND Z clr
+ * LS: C clr AND Z set
+ */
+
+
+debug_armCondCodeTable:
+ /* EQ, NE, HS/CS, LO/CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV */
+ .byte 0x40, 0x04, 0x20, 0x02, 0x80, 0x08, 0x10, 0x01, 0x24, 0x42, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00
+
+/* ARM Complex Condition Code Mapping Table
+ * Converts Instruction encoding to SPSR Flags.
+ * b31 b30 b29 b28
+ * N Z C V
+ * Indexed according to Instruction Encoding order (pg 30, Table 6, ATMEL ARM7TDMI Data Sheet)
+ * for GE, LT, GT and LE instructions only
+ * Condition Code stored in the following order:
+ * b7 b6 b5 b4 b3 b2 b1 b0
+ * - - - ANDOR - Z set AND N==V (bit set = 1)
+ * - - - ANDOR - Z clr OR N!=V (bit clr = 0)
+ *
+ * GE: N == V
+ * LT: N != V
+ * GT: Z clr AND (N == V)
+ * LE: Z set OR (N != V)
+ */
+
+#define COMPLEX_CONDCODE_START 0x0A
+#define COMPLEX_CONDCODE_NEQV_MASK 0x01
+#define COMPLEX_CONDCODE_AND_MASK 0x02
+#define COMPLEX_CONDCODE_ZSET_MASK 0x04
+#define COMPLEX_CONDCODE_ANDOR_MASK 0x10
+
+#define COMPLEX_CONDCODE_NFLAG 0x08
+#define COMPLEX_CONDCODE_ZFLAG 0x04
+#define COMPLEX_CONDCODE_CFLAG 0x02
+#define COMPLEX_CONDCODE_VFLAG 0x01
+
+
+debug_armComplexCCTable:
+ /* GE, LT, GT, LE */
+ .byte 0x01, 0x00, 0x15, 0x12
+
+.code 32
+.text
+.align 4
+ .extern __breakpoints_num__
+ .extern dbg__hasDebugMsg /* Check for message from the communications link */
+ .extern dbg__getDebugMsg /* Read a message from the communications link */
+ .extern dbg__putDebugMsg /* Write a message to the communications link */
+ .extern dbg__requestRetransmission /* Request Retransmission due to Checksum Error */
+ .extern dbg__runloopTasks /* Platform specific Run Loop processing */
+
+
+/* The Debugger Interface can handle a total of (n-1) Breakpoint States and 1 Single Stepping State,
+ * where n is a power of 2. The value of n is given by __breakpoints_num__ defined in the linker file.
+ *
+ * In addition, a Debugger Stack contains the User Mode Register Stack Frame + SPSR + Bkpt Instr Addr.
+ * These are currently stored in the .stack area in RAM, so there is no fixed address
+ * location that is used for this purpose.
+ *
+ * The Breakpoint feature assumes that the program is executed in RAM. It is not possible
+ * to set dynamic breakpoints for programs executed from Flash in the AT91SAM7S which lacks
+ * instruction breakpointing support in hardware without using JTAG. The only type of breakpoints
+ * that can be supported in Flash based programs are Static (predefined) breakpoints inserted into
+ * the code.
+ *
+ * Each Breakpoint State i is a struct comprising the Breakpoint Address + Memory Contents
+ * stored in 8 bytes as:
+ * [High Memory Address]
+ * ADDR [i*8+4]: Memory Contents (32 bits)
+ * ADDR [i*8]: Breakpoint Address (31 bits, b0 = THUMB flag [not implemented yet])
+ * [Low Memory Address]
+ *
+ * A Non-zero Breakpoint Address means that the breakpoint is active, whereas the memory contents
+ * contains the instruction which resided at that address initially (now replaced by a BKPT <index>
+ * instruction).
+ * Note: Currently it is not possible to resume execution of a program with breakpoints enabled
+ * after a RESET, since the RESET will clear all contents of the stack, destroying the instruction
+ * contained in a given breakpoint.
+ * Fortunately the NXT will also need to reload the program into RAM so this is not expected to be
+ * an issue.
+ *
+ * The Memory Map for the Debugger State is as follows:
+ *
+ * [High Memory Address] __breakpoints_end__
+ * Breakpoint 07 State
+ * Breakpoint 06 State
+ * ...
+ * Breakpoint 02 State
+ * Breakpoint 01 State
+ * Single Step State __debugger_stack__ / __breakpoints_start__
+ * Previous Mode R15
+ * Previous Mode R14
+ * Previous Mode R13
+ * ...
+ * User Mode R02
+ * User Mode R01
+ * User Mode R00
+ * Previous Mode CPSR (UNDEF SPSR)
+ * UNDEF Next Instr Addr __debugger_stack_bottom__
+ * [Low Memory Address]
+ *
+ * Each Breakpoint State will initially be zeroed.
+ *
+ */
+ /* FIXME: The Debugger Stack Frame is probably not 100% consistent with the order that
+ GDB expects in the g/G messages. CSPR is probably located above R15 */
+
+#ifndef __NXOS__
+/****************************************************************************
+ *
+ * GDB Debugger Invocation Routine for NXT Firmware
+ *
+ ****************************************************************************/
+ .code 16
+ .align 2
+ .global cCommHandleDebug
+ .thumb_func
+/* cCommHandleDebug
+ * Switch Mode to Debugger.
+ * Used by NXT Firmware only
+ *
+ * UWORD cCommHandleDebug(UBYTE *pInBuf, UBYTE CmdBit, UWORD MsgLength);
+ *
+ * This routine is called from cCommInterprete either in normal operation mode (SVC)
+ * or else when we're in debug mode (ABORT) which uses the cCommCtrl() routine to handle
+ * I/O with the Host.
+ *
+ * On entry, the message is copied from the NXT buffer into our own buffers.
+ *
+ * If this is accessed from normal operation mode, we need to switch mode to
+ * ABORT mode to handle the incoming message using a Manual Breakpoint instruction.
+ * When DEBUG is exited, the execution resumes from the instruction following the Breakpoint.
+ */
+cCommHandleDebug:
+/* Arg Registers are not preserved since this is invoked explicitly */
+ push {lr} /* store arg registers */
+ bl dbg__copyNxtDebugMsg /* setup Debugger Message Buffers, validate input, CPSR returned in R0 */
+ _dbg_getmode r0 /* Get Debug Mode */
+ cmp r0, #(TRUE & BYTE0) /* Confine it to Byte size */
+
+ /* If Debug Mode is TRUE, this means that we're already running the Debugger */
+ beq _cCommHandleDebug_cont
+ /* Else, we're in normal operation mode (SVC), or other mode (??!) and need to force a switch to Debug mode */
+ dbg__bkpt_thumb
+_cCommHandleDebug_cont:
+ mov r0, #0 /* FIXME: Return Status */
+ pop {r1} /* Can't Pop LR directly */
+ bx r1 /* Safe code: actually we should be able to Pop PC since the caller is Thumb Mode */
+
+ .ltorg
+#endif
+
+/****************************************************************************
+ *
+ * GDB Debugger Init and Breakpoint Handler Routines
+ *
+ ****************************************************************************/
+ .code 32
+ .align 4
+ .global dbg__bkpt_init
+/* dbg__bkpt_init
+ * GDB set_debug_traps() routine
+ */
+dbg__bkpt_init:
+ push {lr}
+ bl _dbg__clear_breakpoints
+ mov r2, #0
+ ldr r1, =debug_curr_breakpoint
+ str r2, [r1]
+ ldr r0, =debug_InMsgBuf
+ strb r2, [r0]
+ ldr r1, =debug_OutMsgBuf
+ strb r2, [r1]
+ bl dbg__comm_init /* Pass R0: Rx Buffer, R1: Tx Buffer to comm submodule */
+
+/* FIXME: Initialize other stuff here */
+ _dbg_setstate DBG_INIT
+ _dbg_setmode FALSE /* Debug Mode = False */
+ pop {lr}
+ bx lr /* Must return via BX; may have been called from Thumb mode (NXT Firmware) */
+
+
+/* _dbg__flush_icache
+ * Flush the Instruction cache
+ * Defined by GDB Stub, but not needed for ARMv4T architecture
+ */
+_dbg__flush_icache:
+ /* nop */
+ bx lr
+
+
+ .global dbg__thumb_bkpt_handler
+/* dbg__thumb_bkpt_handler
+ * GDB handle_exception() routine (Thumb Mode)
+ */
+dbg__thumb_bkpt_handler:
+/* On entry, r0 contains breakpoint index value */
+ mov r4, #BKPT16_AUTO_BKPT
+ and r4, r0, #BKPT16_AUTO_BKPT /* keep AUTO flag value in r4 */
+ bic r0, r0, #BKPT16_AUTO_BKPT /* mask out AUTO flag */
+ _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */
+ ldr r1, =BKPT16_MANUAL_BKPT
+ teq r0, r1
+ beq _process_manual_breakpoint_thumb
+ ldr r1, =__breakpoints_num__
+ cmp r0, r1 /* Sanity check that index is in range */
+ bhs dbg__bkpt_offset_outofrange
+/* Valid index value found */
+ teq r4, #0 /* Check if AUTO flag set */
+ bne _process_auto_breakpoint
+/* else */
+ _dbg_setstate DBG_NORMAL_BKPT_THUMB
+ b _process_normal_breakpoint
+
+ .global dbg__arm_bkpt_handler
+/* dbg__arm_bkpt_handler
+ * GDB handle_exception() routine (ARM Mode)
+ */
+dbg__arm_bkpt_handler:
+/* On entry, r0 contains breakpoint index value */
+ mov r4, #BKPT32_AUTO_BKPT
+ and r4, r0, #BKPT32_AUTO_BKPT /* keep AUTO flag value in r4 */
+ bic r0, r0, #BKPT32_AUTO_BKPT /* mask out AUTO flag */
+ _dbg_setcurrbkpt_index r0 /* keep current breakpoint index in memory */
+ ldr r1, =BKPT32_MANUAL_BKPT
+ teq r0, r1
+ beq _process_manual_breakpoint_arm
+ ldr r1, =__breakpoints_num__
+ cmp r0, r1 /* Sanity check that index is in range */
+ bhs dbg__bkpt_offset_outofrange
+/* Valid index value found */
+ teq r4, #0 /* Check if AUTO flag set */
+ bne _process_auto_breakpoint
+/* else */
+ _dbg_setstate DBG_NORMAL_BKPT_ARM
+/* b _process_normal_breakpoint */
+
+_process_normal_breakpoint:
+ bl _dbg__restore_breakpoints
+ bl _dbg__restore_singlestep
+ bl _dbg__clear_singlestep
+ bl _dbg__flush_icache
+ b dbg__bkpt_waitCMD
+
+_process_auto_breakpoint:
+/* Load Auto BKPT for Breakpoint index given in r0 */
+ _index2bkptindex_addr r0, r1 /* Calculate Breakpoint Entry Address */
+ ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */
+ teq r1, #0 /* Check that Breakpoint is active */
+ beq dbg__bkpt_inactive
+ bl _dbg__activate_one_breakpoint
+ bl _dbg__restore_singlestep
+ bl _dbg__clear_singlestep
+ b __dbg__resume_execution
+
+_process_manual_breakpoint_thumb:
+ _dbg_setstate DBG_MANUAL_BKPT_THUMB
+ b dbg__bkpt_waitCMD
+
+_process_manual_breakpoint_arm:
+ _dbg_setstate DBG_MANUAL_BKPT_ARM
+/* b dbg__bkpt_waitCMD */
+
+dbg__bkpt_inactive:
+/* b dbg__bkpt_waitCMD */
+
+dbg__bkpt_offset_outofrange:
+/* b dbg__bkpt_waitCMD */
+
+ .global dbg__bkpt_waitCMD
+/* dbg__bkpt_waitCMD
+ * GDB Stub Remote Command Handler
+ */
+
+/****************************************************************************
+ *
+ * GDB Server Command Processing Routines
+ *
+ ****************************************************************************/
+dbg__bkpt_waitCMD:
+ /* We enter this code section when a Breakpoint Triggers */
+ _dbg_setmode TRUE /* Debug Mode = True */
+ _dbg_getstate r0
+ cmp r0, #DBG_CONFIGURED
+ blo dbg__bkpt_waitCMD_cont /* Not configured yet, don't send Breakpoint Signal Response */
+ bl _dbg__cmd_Status /* Send signal response to the GDB server */
+
+dbg__bkpt_waitCMD_cont:
+ bl dbg__runloopTasks /* Execute housekeeping tasks while in ABRT mode */
+ bl dbg__getDebugMsg /* Read new message from Debugger, buflen in R0, 0 if none, -1 if error, msgbuf pointer in R1 */
+ cmp r0, #0
+ beq dbg__bkpt_waitCMD_cont /* No message yet, do housekeeping tasks */
+ blt __dbg__procChecksumError /* Message invalid, checksum error? */
+/* Message now has $<packet info>\0 */
+ mov r4, r1 /* Use R4 as buffer pointer */
+ ldrb r0, [r4], #1 /* Look for '$' */
+ teq r0, #MSGBUF_STARTCHAR
+ movne r0, #MSG_ERRFORMAT /* Message Format invalid (not '$') */
+ bne _dbg__cmdError /* Shouldn't happen */
+ ldrb r0, [r4], #1 /* Look for command char */
+ bl _dbg__cmdChar2Index /* Index in R0 */
+ mov r1, #CMDINDEX_OUTOFRANGE
+ teq r0, r1
+ bne _dbg__cmdExists /* Found valid command, execute it */
+_dbg_unknown_command:
+ bl _dbg__nop /* Command character not recognized, send empty response to GDB server */
+ b dbg__bkpt_waitCMD_cont
+
+#if 0
+ moveq r0, #MSG_UNKNOWNCMD /* Out of range, Command character not recognized */
+ beq _dbg__cmdError /* Send response to GDB server */
+#endif
+
+_dbg__cmdExists:
+ mov r3, r0 /* put Command Handler Index in R3 */
+ mov r0, r4 /* R0 now contains Input Message Buffer Parameter Pointer (previously in R4) */
+ _dbg_jumpTableHandler debug_cmdJumpTable, r2, r3 /* Call Command Handler Routine, use R2 as jump address pointer */
+ b dbg__bkpt_waitCMD_cont
+
+__dbg__procChecksumError:
+ _dbg_outputRetransmitFlag
+ bl dbg__requestRetransmission /* Request message retransmission from GDB server */
+ cmp r0, #0
+ beq dbg__bkpt_waitCMD_cont /* Sending of retransmission request succeeded */
+ bl dbg__runloopTasks /* Service run loop tasks */
+ b __dbg__procChecksumError /* Retry retransmission */
+
+_dbg__cmdError:
+ _dbg_outputMsgStatusErr
+ bl dbg__putDebugMsg /* Send error response to the GDB server */
+ b dbg__bkpt_waitCMD_cont
+
+/* _dbg__cmdChar2Index
+ * Convert Command Character to Jump Table Index
+ * On entry:
+ * r0: command character
+ * On exit:
+ * r0: jump table index (-1 for command not found)
+ * R1: destroyed
+ * R2: destroyed
+ * R3: destroyed
+ */
+_dbg__cmdChar2Index:
+ mov r1, r0 /* Copy command character to r1 */
+ mov r0, #0 /* Clear return value */
+ ldr r3, =debug_cmdIndexTable /* Convert command to index using r3 as Index Lookup Address Pointer */
+1: ldrb r2, [r3, r0] /* Get table entry */
+ teq r2, #0
+ moveq r0, #CMDINDEX_OUTOFRANGE /* End of Index Table, Not found */
+ beq _exit_cmdIndexTable
+ teq r1, r2
+ addne r0, #1 /* Increment Index */
+ bne 1b /* No match, skip to next command char */
+_exit_cmdIndexTable:
+ bx lr
+
+/* __dbg__cmdParamLen
+ * Determines the length of the parameter buffer for a given command
+ * On entry:
+ * R0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * On exit:
+ * R0: Address of parameter buffer (preserved)
+ * R1: length
+ */
+__dbg__cmdParamLen:
+ stmfd sp!, {r0,r2,lr} /* R2: scratch register */
+ mov r1, #0
+1: ldrb r2, [r0], #1
+ teq r2, #0
+ addne r1, r1, #1
+ bne 1b
+ ldmfd sp!, {r0,r2,pc}
+
+/* __dbg__procCmdOk
+ * Common subroutine exit stub to return Command Ok Status for Command Handlers
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ *
+ */
+__dbg__procCmdOk:
+ _dbg_outputMsgStatusOk
+ b __dbg__sendDebugMsgExit
+
+/* __dbg__procUnimplementedError
+ * Common subroutine exit stub to handle Unimplemented GDB Command Error
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ * Note: GDB Remote Protocol specifies returning blank message ('+$')
+ *
+ */
+__dbg__procUnimplementedError:
+ _dbg_outputMsgValidResponse
+ b __dbg__sendDebugMsgExit
+
+/* __dbg__procCmdParamError
+ * Common subroutine exit stub to handle Command Parameter Error for Command Handlers
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ *
+ */
+__dbg__procCmdParamError:
+ mov r1, #MSG_UNKNOWNPARAM
+ b __dbg__procErrorMsg
+
+/* __dbg__procCmdReturnInputLengthError
+ * Common subroutine exit stub to handle Command Input Length Error for Command Handlers
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ *
+ */
+__dbg__procCmdReturnInputLengthError:
+ mov r1, #MSG_ERRINLENGTH
+ b __dbg__procErrorMsg
+
+/* __dbg__procCmdReturnOutputLengthError
+ * Common subroutine exit stub to handle Command Return Output Error for Command Handlers
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ *
+ */
+__dbg__procCmdReturnOutputLengthError:
+ mov r1, #MSG_ERROUTLENGTH
+ b __dbg__procErrorMsg
+
+/* __dbg__procBreakpointAddrError
+ * Common subroutine exit stub to handle Breakpoint Address Error for Breakpoint Insert/Remove Handlers
+ * DO NOT CALL THIS STUB DIRECTLY! It Assumes that the return address is in the stack.
+ *
+ */
+__dbg__procBreakpointAddrError:
+ mov r1, #MSG_UNKNOWNBRKPT
+ b __dbg__procErrorMsg
+
+
+__dbg__procErrorMsg:
+ __dbg_outputErrMsg
+ /* b __dbg__sendDebugMsgExit */
+
+__dbg__sendDebugMsgExit:
+ bl dbg__putDebugMsg /* Send error response to the GDB server */
+ ldmfd sp!, {pc}
+
+/* _dbg__cmd_Status
+ * Status Command Handler
+ * On entry:
+ * r0: parameter buffer (contents after '$' and '<cmdchar>')
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__cmd_Status:
+ stmfd sp!, {lr}
+ _dbg_outputMsgStatusSig 0x0 /* FIXME: Dummy Signal number */
+ b __dbg__sendDebugMsgExit
+
+/* _dbg__cmd_Query
+ * Query Command Handler
+ * On entry:
+ * r0: parameter buffer (contents after '$' and '<cmdchar>')
+ * [varied, see GDB General Packets query docs]
+ * http://sourceware.org/gdb/current/onlinedocs/gdb/General-Query-Packets.html
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__cmd_Query:
+ stmfd sp!, {lr}
+ _dbg_setstate DBG_CONFIGURED /* We have exchanged query messages with the GDB server */
+ b __dbg__procUnimplementedError /* FIXME: return an empty message to GDB (no modifiable settings) */
+
+
+/* _dbg__cmd_GetOneReg
+ * Get One Register Value Command Handler
+ * Valid command parameter x is from '0' to 'F' for User Mode Registers R0-R15
+ * CPSR register is '!'
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * x
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ *
+ */
+_dbg__cmd_GetOneReg:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+ teq r1, #CMD_REG_GETONE_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ ldrb r0, [r0] /* Load Register index parameter */
+ teq r0, #MSGBUF_CPSRREG /* Check for CPSR register indicator */
+ moveq r0, #REG_CPSR /* Use CPSR Enum (-1) */
+ beq _dbg__proc_getRegister /* Handle User CPSR */
+ bl char2hex /* Convert to Hex value (assume input is valid) */
+ cmp r0, #NIBBLE0 /* sanity check, (though it is not foolproof as input char in 0x0-0xF (ctrl-chars) will pass through) */
+ bhi __dbg__procCmdParamError /* Non-hex char, report error */
+
+_dbg__proc_getRegister:
+ mov r3, r0 /* Keep register index safe */
+ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */
+ mov r1, r3 /* Move register index value to R1 */
+ bl _dbg_outputOneRegValue /* update output buffer */
+ _asciiz r0, r1
+ bl dbg__putDebugMsg /* Send response to the GDB server */
+ ldmfd sp!, {pc}
+
+/* _dbg_outputOneRegValue
+ * Given Register Enum (-1: CPSR, 0-F: R0-R15), output hex char to buffer
+ * On entry:
+ * r0: output message buffer pointer
+ * r1: register enum (-1, 0-F)
+ * On exit:
+ * r0: updated (points to next character slot at end of Output Buffer)
+ * r1: original output message buffer pointer
+ * r2: destroyed
+ */
+_dbg_outputOneRegValue:
+ stmfd sp!, {lr}
+ add r2, r1, #DBGSTACK_USERREG_INDEX /* Convert register enum to Debug Stack index */
+ _getdbgregisterfromindex r2, r1 /* Retrieve Register contents into R1 */
+#ifdef __BIG_ENDIAN__
+ bl word2ascii_be /* Convert and put hex chars into Output Message Buffer */
+#else
+ bl word2ascii_le /* Convert and put hex chars into Output Message Buffer */
+#endif
+ ldmfd sp!, {pc}
+
+/* _dbg__cmd_GetAllRegs
+ * Get All Register Values Command Handler
+ * Output Buffer returns register values in the order: User R0, R1, R2, ..., R15, FPSCR (dummy), CPSR
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * <NULL> (no parameters)
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__cmd_GetAllRegs:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+ teq r1, #CMD_REG_GETALL_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+
+ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */
+
+ /* We must return R0-R15, then CPSR */
+ mov r3, #REG_R0 /* Output User Register Values first */
+1: mov r1, r3
+ bl _dbg_outputOneRegValue /* update output buffer */
+ add r3, r3, #1 /* increment index */
+ cmp r3, #REG_PC
+ bls 1b /* process all the registers */
+_get_cpsr:
+#if 0
+ /* GDB was not happy when this was added */
+ /* Output Dummy FPSCR value */
+ mov r1, #0
+#ifdef __BIG_ENDIAN__
+ bl word2ascii_be /* Convert and put hex chars into Output Message Buffer */
+#else
+ bl word2ascii_le /* Convert and put hex chars into Output Message Buffer */
+#endif
+#endif
+ mov r1, #REG_CPSR /* Output User CPSR Value last */
+ bl _dbg_outputOneRegValue /* update output buffer */
+
+ _asciiz r0, r1
+ bl dbg__putDebugMsg /* Send response to the GDB server */
+ ldmfd sp!, {pc}
+
+/* _dbg_setOneRegValue
+ * Given Register Enum (-1: CPSR, 0-F: R0-R15), set register to buffer contents
+ * On entry:
+ * r0: parameter buffer pointer
+ * r1: register enum (-1, 0-F)
+ * On exit:
+ * r0: (Updated) Address of parameter in buffer
+ * r1, r2, r3: destroyed
+ */
+_dbg_setOneRegValue:
+ stmfd sp!, {lr}
+ add r2, r1, #DBGSTACK_USERREG_INDEX /* Convert register enum to Debug Stack index */
+#ifdef __BIG_ENDIAN__
+ bl ascii2word_be
+#else
+ bl ascii2word_le
+#endif
+ /* R0: value, R1: pointer to next char in buffer */
+ _setdbgregisterfromindex r2, r0, r3 /* Set Register contents in R0, using index in R2, and scratch register R3 */
+ mov r0, r1 /* Copy buffer pointer to next parameter to R0 for return value */
+ ldmfd sp!, {pc}
+
+
+/* _dbg__cmd_SetOneReg
+ * Set One Register Value Command Handler
+ * Valid command parameter x is from '0' to 'F' for User Mode Registers R0-R15
+ * CPSR register is '!'
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * x=rrrr
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ *
+ */
+_dbg__cmd_SetOneReg:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+ teq r1, #CMD_REG_SETONE_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ mov r3, r0 /* Keep parameter buffer address in R3 */
+ ldrb r1, [r3], #1 /* Load Register enum parameter */
+ _check_msgassignment r3
+ bne __dbg__procCmdParamError /* Can't find '=' */
+ teq r1, #MSGBUF_CPSRREG /* Check for CPSR register indicator */
+ moveq r1, #REG_CPSR /* Use CPSR enum (-1) */
+ beq _dbg__proc_setRegister /* Handle User CPSR */
+ mov r0, r1 /* Move register enum to R0 for subsequent processing */
+ bl char2hex /* Convert to Hex value (assume input is valid) */
+ cmp r0, #NIBBLE0 /* sanity check, (though it is not foolproof as input char in 0x0-0xF (ctrl-chars) will pass through) */
+ bhi __dbg__procCmdParamError /* Non-hex char, report error */
+ mov r1, r0 /* move register enum to R1 to call _dbg_setOneRegValue */
+
+_dbg__proc_setRegister:
+ mov r0, r3 /* Retrieve parameter buffer pointer */
+ bl _dbg_setOneRegValue
+ b __dbg__procCmdOk
+
+/* _dbg__cmd_SetAllReg
+ * Set All Register Values Command Handler
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * rrrrRRRRrrrr... (17 registers)
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ *
+ */
+_dbg__cmd_SetAllRegs:
+ /* Assumes that the registers are in the sequence R0, R1, ... R15, CPSR */
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen /* R0: pointer to parameters in buffer */
+ teq r1, #CMD_REG_SETALL_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ mov r4, #REG_R0 /* R4: register enum, starting with enum for R0 */
+1: mov r1, r4 /* Copy enum to R1; R0 already points to current parameter */
+ bl _dbg_setOneRegValue /* R0: next parameter address pointer */
+ add r4, r4, #1 /* increment index */
+ cmp r4, #REG_PC
+ bls 1b
+
+_set_cpsr:
+#if 0
+ /* GDB was not happy when this was added */
+ /* Read dummy FPSCR value (ignored) */
+#ifdef __BIG_ENDIAN__
+ bl ascii2word_be
+#else
+ bl ascii2word_le
+#endif
+ mov r0, r1 /* Copy buffer pointer to next parameter to R0 for return value */
+#endif
+ mov r1, #REG_CPSR /* Use CPSR enum (-1) */
+ bl _dbg_setOneRegValue /* R0: next parameter address pointer */
+ ldrb r0, [r0]
+ teq r0, #0 /* Look for ASCIIZ character to terminate loop */
+ beq __dbg__procCmdOk
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+
+/* _dbg__nop
+ * NOP Command Handler (placeholder)
+ * On entry:
+ * r0: parameter buffer (contents after '$' and '<cmdchar>')
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__nop:
+ stmfd sp!, {lr}
+ b __dbg__procUnimplementedError
+
+/* _dbg__cmd_ReadMem
+ * Read Memory Contents Command Handler
+ * Output Buffer returns memory contents
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * AA..AA,LLLL
+ * On exit:
+ * r0, r1, r2, r3, r4: destroyed
+ */
+_dbg__cmd_ReadMem:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+#if 0
+ teq r1, #CMD_MEM_READ_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ bl ascii2word_be /* convert ASCII address location to Hex (in R0), R1 has address of next buffer char */
+#endif
+ bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */
+ mov r3, r0 /* Keep Address location in R3 */
+ _check_msgseparator r1
+ bne __dbg__procCmdParamError /* Can't find ',' */
+ mov r0, r1 /* move buffer pointer to R0 for subsequent processing */
+ bl ascii2hex_varlen_be /* convert ASCII length to Hex (in R0), R1 has address of next buffer char */
+ cmp r0, #CMD_MEM_MAXREADBYTES /* Don't overflow our buffer (2 x CMD_MEM_MAXREADBYTES) */
+ bhi __dbg__procCmdReturnOutputLengthError /* Requested Length greater than buffer size, return error */
+ mov r4, r0 /* Keep numbytes in R4 */
+ /* FIXME: Should validate the address? */
+
+ _dbg_outputMsgValidResponse /* R0: address of output message buffer data pointer (after response prefix) */
+1: ldrb r1, [r3], #1
+ bl byte2ascii /* update output buffer */
+ subs r4, r4, #1
+ bne 1b
+
+ _asciiz r0, r1
+ bl dbg__putDebugMsg /* Send response to the GDB server */
+ ldmfd sp!, {pc}
+
+
+/* _dbg__cmd_WriteMem
+ * Write Memory Contents Command Handler
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * AA..AA,LLLL::bb..bb
+ * On exit:
+ * r0, r1, r2, r3, r4: destroyed
+ */
+_dbg__cmd_WriteMem:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+#if 0
+ cmp r1, #CMD_MEM_WRITE_MINPARAMLEN /* Check for correct (minimum) length */
+ blo __dbg__procCmdParamError /* Unexpected input, report error */
+ sub r4, r1, #CMD_MEM_WRITE_MINPARAMLEN /* R4: Number of ASCII Hex chars for byte writes */
+ bl ascii2word_be /* convert ASCII address location to Hex (in R0), R1 has address of next buffer char */
+#endif
+ bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */
+ mov r3, r0 /* Keep Address location in R3 */
+ _check_msgseparator r1
+ bne __dbg__procCmdParamError /* Can't find ',' */
+ mov r0, r1 /* move buffer pointer to R0 for subsequent processing */
+ bl ascii2hex_varlen_be /* convert ASCII length to Hex (in R0), R1 has address of next buffer char */
+ cmp r0, r4, asr #1 /* is Number of bytes to write == (number of ASCII Hex Chars / 2)? */
+ bne __dbg__procCmdParamError /* Number of bytes does not match argument length */
+ cmp r0, #CMD_MEM_MAXWRITEBYTES /* Don't overflow our buffer (2 x CMD_MEM_MAXWRITEBYTES) */
+ bhi __dbg__procCmdReturnInputLengthError /* Requested Length greater than buffer size, return error */
+ mov r4, r0 /* Keep numbytes in R4 */
+ /* FIXME: Should validate the address? */
+ _check_msgargument r1
+ bne __dbg__procCmdParamError /* Can't find ':' */
+
+1: mov r0, r1
+ bl ascii2byte /* convert ASCII Hex value into byte value */
+ strb r0, [r3], #1 /* Store into memory location */
+ subs r4, r4, #1
+ bne 1b
+ b __dbg__procCmdOk
+
+/* _dbg__cmd_Continue
+ * Continue User Program Execution Command Handler
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * Optional: AA..AA
+ * On exit:
+ * r0, r1, r2: destroyed
+ * Note: This routine does not return to caller. Instead it switches
+ * operating mode to UNDEF and returns to previously active program
+ */
+_dbg__cmd_Continue:
+ bl __dbg__cmdParamLen
+ cmp r1, #CMD_REG_CONTINUE_PARAMLEN /* Check for correct parameter length */
+ beq _dbg__cmd_processContinue /* Continue from current PC */
+#if 0
+ cmp r1, #CMD_REG_CONTINUEFROM_PARAMLEN /* Check for correct parameter length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ bl ascii2word_be /* convert ASCII address location to Hex (in R0), R1 has address of next buffer char */
+#endif
+ bl ascii2hex_varlen_be /* convert ASCII address to Hex (in R0), R1 has address of next buffer char */
+ /* Continue from Specified Address */
+ mov r2, #DBGSTACK_NEXTINSTR_INDEX /* The Next Instruction Pointer for Resume is in index 0 of the Debug Stack */
+ _setdbgregisterfromindex r2, r0, r1 /* Set Register contents in R0, using index in R2, and scratch register R1 */
+
+_dbg__cmd_processContinue:
+/* FIXME: Currently we assume that the trigger is a Manual Breakpoint, i.e., no need to
+ restore instructions to memory, and handle auto-breakpoints (needed to reenable the memory
+ breakpoint that was triggered) */
+
+@@@@@
+#if 0
+/* Not part of the GDB Remote Protocol spec. Messages are sent only when system halts, not when we resume */
+__dbg__sendOkBeforeResume:
+ _dbg_outputMsgStatusOk
+ bl dbg__putDebugMsg /* Send Ok response to the GDB server */
+ cmp r0, #0
+ beq _dbg__switch2undefmode /* Sending of retransmission request succeeded */
+ bl dbg__runloopTasks /* Service run loop tasks */
+ b __dbg__sendOkBeforeResume /* Retry retransmission */
+#endif
+
+_dbg__switch2undefmode:
+ msr cpsr_c, #(MODE_UND | CPSR_FIQ | CPSR_IRQ) /* Configure Undef Mode */
+ _dbg_setmode FALSE /* Debug Mode = False */
+ ldr lr, =resume_execution
+ mov pc, lr /* Exit via UNDEF mode */
+
+
+/* _dbg__proc_brkpt_params
+ * Process Breakpoint Parameters
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * t,AA..AA,k
+ * On exit:
+ * r0: non-zero = breakpoint address; 0 = parameter error
+ * r1: destroyed
+ * r2: destroyed
+ * r3: destroyed
+ */
+_dbg__proc_brkpt_params:
+ /* FIXME: Add support for watchpoints */
+ stmfd sp!, {lr}
+ mov r3, r0 /* Keep parameter buffer address in R3 */
+ ldrb r0, [r3], #1 /* get breakpoint type t */
+ bl char2hex
+ cmp r0, #CMD_BKPT_TYPE_BREAK_MEMORY
+ bne _dbg__proc_brkpt_params_error /* We only support memory breakpoints for now */
+ _check_msgseparator r3
+ bne _dbg__proc_brkpt_params_error /* Something wrong with the parameters */
+ mov r0, r3 /* Check Address */
+ bl ascii2word_be /* R0: value, R1: pointer to next char slot */
+ mov r3, r0 /* Keep breakpoint address in R3 */
+ _check_msgseparator r1
+ bne _dbg__proc_brkpt_params_error /* Something wrong with the parameters */
+ ldrb r0, [r1], #1 /* get breakpoint kind k */
+ bl char2hex
+ cmp r0, #CMD_BKPT_KIND_THUMB
+ orreq r3, r3, #1 /* Mark Thumb breakpoints */
+ beq _exit_dbg__proc_brkpt_params
+ cmp r0, #CMD_BKPT_KIND_ARM
+ beq _exit_dbg__proc_brkpt_params /* ARM breakpoint */
+
+_dbg__proc_brkpt_params_error:
+ mov r3, #0 /* Unrecognized breakpoint type */
+_exit_dbg__proc_brkpt_params:
+ mov r0, r3 /* return breakpoint address */
+ ldmfd sp!, {pc}
+
+/* _dbg__cmd_insert_breakpoint
+ * Add Breakpoint
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * t,AA..AA,k
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__cmd_insert_breakpoint:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+ teq r1, #CMD_BKPT_INSERT_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ bl _dbg__proc_brkpt_params /* R0: Breakpoint Address */
+ teq r0, #0
+ beq __dbg__procBreakpointAddrError /* Thumb2 instructions, or unknown kind */
+ mov r3, r0 /* Keep breakpoint address in R3 */
+ mov r0, #0 /* Empty Breakpoint entry */
+ bl _dbg_find_breakpoint_slot /* Look for an available breakpoint slot, return index in R0 */
+ cmp r0, #CMD_BKPT_NOTFOUND
+ beq __dbg__procBreakpointAddrError /* No empty slot! */
+ mov r1, r3 /* Move breakpoint address to R1 */
+ bl _dbg__install_one_breakpoint /* r0: index, r1: instruction address */
+ b __dbg__procCmdOk
+
+/* _dbg__cmd_remove_breakpoint
+ * Remove Breakpoint
+ * On entry:
+ * r0: parameter buffer pointer (contents after '$' and '<cmdchar>')
+ * t,AA..AA,k
+ * On exit:
+ * r0, r1, r2, r3: destroyed
+ */
+_dbg__cmd_remove_breakpoint:
+ stmfd sp!, {lr}
+ bl __dbg__cmdParamLen
+ teq r1, #CMD_BKPT_REMOVE_PARAMLEN /* Check for correct length */
+ bne __dbg__procCmdParamError /* Unexpected input, report error */
+ bl _dbg__proc_brkpt_params /* R0: Breakpoint Address */
+ teq r0, #0
+ beq __dbg__procBreakpointAddrError /* Thumb2 instructions, or unknown kind */
+ bl _dbg_find_breakpoint_slot /* Look for matching breakpoint slot, return index in R0 */
+ cmp r0, #CMD_BKPT_NOTFOUND
+ beq __dbg__procBreakpointAddrError /* Specified Breakpoint not found! */
+ _index2bkptindex_addr r0, r1 /* Calculate Breakpoint Entry Address */
+ mov r0, r1 /* Move it to R0 for subroutine call */
+ bl _dbg__clear_one_breakpoint /* R0: address of breakpoint to clear */
+ b __dbg__procCmdOk
+
+
+/* _dbg__cmd_run
+ * Continue execution of program
+ */
+_dbg__cmd_run:
+ bl _dbg__activate_breakpoints
+ b __dbg__resume_execution
+
+/* _dbg__cmd_step
+ * Single Step execution of program
+ */
+_dbg__cmd_step:
+ bl _dbg_next_instruction_addr /* next instruction address returned in r1 */
+ bl _dbg__install_singlestep /* Setup Single Step */
+ bl _dbg__activate_singlestep
+ b __dbg__resume_execution
+
+/* _dbg__cmd_cont
+ * Continue execution of program.
+ * If this is a Normal Breakpoint, then we need to install an Autobreakpoint at next instruction address
+ * and resume from current (Breakpoint) exception address
+ * Else (it is a Manual Breakpoint)
+ * We need to resume from the next instruction address
+ */
+_dbg__cmd_cont:
+/* FIXME: What happens if we call this when we did not stop at a Breakpoint previously? */
+ _dbg_getstate r0
+ ldr r1, =DBG_MANUAL_BKPT_ARM
+ teq r0, r1
+ beq __dbg_is_manual_breakpoint
+
+ bl _dbg_next_instruction_addr /* next instruction address returned in r1 */
+ bl _dbg__install_singlestep /* Setup Single Step, next instruction address returned in r1 */
+ _dbg_getcurrbkpt_index r0 /* load current breakpoint index in memory */
+ bl _dbg__activate_autobreakpoint /* pass next instruction address in r1 */
+ b __dbg__resume_execution
+
+__dbg_is_manual_breakpoint:
+ bl _dbg_next_instruction_addr /* Skip Manual Breakpoint Instruction(s) */
+ bl _dbg__activate_breakpoints
+ b __dbg__resume_execution
+
+/****************************************************************************
+// Selected Routines from the eCos arm_stub.c related to next instruction address
+// determination in ARM processors.
+
+//========================================================================
+//
+// arm_stub.c
+//
+// Helper functions for stub, generic to all ARM processors
+//
+//========================================================================
+// ####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+//
+// eCos 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 or (at your option) any later
+// version.
+//
+// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// As a special exception, if other files instantiate templates or use
+// macros or inline functions from this file, or you compile this file
+// and link it with other works to produce a work based on this file,
+// this file does not by itself cause the resulting work to be covered by
+// the GNU General Public License. However the source code for this file
+// must still be made available in accordance with section (3) of the GNU
+// General Public License v2.
+//
+// This exception does not invalidate any other reasons why a work based
+// on this file might be covered by the GNU General Public License.
+// -------------------------------------------
+// ####ECOSGPLCOPYRIGHTEND####
+//========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s): Red Hat, gthomas
+// Contributors: Red Hat, gthomas, jskov
+// Date: 1998-11-26
+// Purpose:
+// Description: Helper functions for stub, generic to all ARM processors
+// Usage:
+//
+//####DESCRIPTIONEND####
+//
+//========================================================================
+
+
+static int
+ins_will_execute(unsigned long ins)
+{
+ unsigned long psr = get_register(PS); // condition codes
+ int res = 0;
+ switch ((ins & 0xF0000000) >> 28) {
+ case 0x0: // EQ
+ res = (psr & PS_Z) != 0;
+ break;
+ case 0x1: // NE
+ res = (psr & PS_Z) == 0;
+ break;
+ case 0x2: // CS
+ res = (psr & PS_C) != 0;
+ break;
+ case 0x3: // CC
+ res = (psr & PS_C) == 0;
+ break;
+ case 0x4: // MI
+ res = (psr & PS_N) != 0;
+ break;
+ case 0x5: // PL
+ res = (psr & PS_N) == 0;
+ break;
+ case 0x6: // VS
+ res = (psr & PS_V) != 0;
+ break;
+ case 0x7: // VC
+ res = (psr & PS_V) == 0;
+ break;
+ case 0x8: // HI
+ res = ((psr & PS_C) != 0) && ((psr & PS_Z) == 0);
+ break;
+ case 0x9: // LS
+ res = ((psr & PS_C) == 0) || ((psr & PS_Z) != 0);
+ break;
+ case 0xA: // GE
+ res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) ||
+ ((psr & (PS_N|PS_V)) == 0);
+ break;
+ case 0xB: // LT
+ res = ((psr & (PS_N|PS_V)) == PS_N) ||
+ ((psr & (PS_N|PS_V)) == PS_V);
+ break;
+ case 0xC: // GT
+ res = ((psr & (PS_N|PS_V)) == (PS_N|PS_V)) ||
+ ((psr & (PS_N|PS_V)) == 0);
+ res = ((psr & PS_Z) == 0) && res;
+ break;
+ case 0xD: // LE
+ res = ((psr & (PS_N|PS_V)) == PS_N) ||
+ ((psr & (PS_N|PS_V)) == PS_V);
+ res = ((psr & PS_Z) == PS_Z) || res;
+ break;
+ case 0xE: // AL
+ res = TRUE;
+ break;
+ case 0xF: // NV
+ if (((ins & 0x0E000000) >> 24) == 0xA)
+ res = TRUE;
+ else
+ res = FALSE;
+ break;
+ }
+ return res;
+}
+
+static unsigned long
+RmShifted(int shift)
+{
+ unsigned long Rm = get_register(shift & 0x00F);
+ int shift_count;
+ if ((shift & 0x010) == 0) {
+ shift_count = (shift & 0xF80) >> 7;
+ } else {
+ shift_count = get_register((shift & 0xF00) >> 8);
+ }
+ switch ((shift & 0x060) >> 5) {
+ case 0x0: // Logical left
+ Rm <<= shift_count;
+ break;
+ case 0x1: // Logical right
+ Rm >>= shift_count;
+ break;
+ case 0x2: // Arithmetic right
+ Rm = (unsigned long)((long)Rm >> shift_count);
+ break;
+ case 0x3: // Rotate right
+ if (shift_count == 0) {
+ // Special case, RORx
+ Rm >>= 1;
+ if (get_register(PS) & PS_C) Rm |= 0x80000000;
+ } else {
+ Rm = (Rm >> shift_count) | (Rm << (32-shift_count));
+ }
+ break;
+ }
+ return Rm;
+}
+
+// Decide the next instruction to be executed for a given instruction
+static unsigned long *
+target_ins(unsigned long *pc, unsigned long ins)
+{
+ unsigned long new_pc, offset, op2;
+ unsigned long Rn;
+ int i, reg_count, c;
+
+ switch ((ins & 0x0C000000) >> 26) {
+ case 0x0:
+ // BX or BLX
+ if ((ins & 0x0FFFFFD0) == 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) {
+ // Destination register is PC
+ if ((ins & 0x0FBF0000) != 0x010F0000) {
+ Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
+ if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
+ if ((ins & 0x02000000) == 0) {
+ op2 = RmShifted(ins & 0x00000FFF);
+ } else {
+ op2 = ins & 0x000000FF;
+ i = (ins & 0x00000F00) >> 8; // Rotate count
+ op2 = (op2 >> (i*2)) | (op2 << (32-(i*2)));
+ }
+ switch ((ins & 0x01E00000) >> 21) {
+ case 0x0: // AND
+ new_pc = Rn & op2;
+ break;
+ case 0x1: // EOR
+ new_pc = Rn ^ op2;
+ break;
+ case 0x2: // SUB
+ new_pc = Rn - op2;
+ break;
+ case 0x3: // RSB
+ new_pc = op2 - Rn;
+ break;
+ case 0x4: // ADD
+ new_pc = Rn + op2;
+ break;
+ case 0x5: // ADC
+ c = (get_register(PS) & PS_C) != 0;
+ new_pc = Rn + op2 + c;
+ break;
+ case 0x6: // SBC
+ c = (get_register(PS) & PS_C) != 0;
+ new_pc = Rn - op2 + c - 1;
+ break;
+ case 0x7: // RSC
+ c = (get_register(PS) & PS_C) != 0;
+ new_pc = op2 - Rn +c - 1;
+ break;
+ case 0x8: // TST
+ case 0x9: // TEQ
+ case 0xA: // CMP
+ case 0xB: // CMN
+ break; // PC doesn't change
+ case 0xC: // ORR
+ new_pc = Rn | op2;
+ break;
+ case 0xD: // MOV
+ new_pc = op2;
+ break;
+ case 0xE: // BIC
+ new_pc = Rn & ~op2;
+ break;
+ case 0xF: // MVN
+ new_pc = ~op2;
+ break;
+ }
+ }
+ }
+ return ((unsigned long *)new_pc);
+ case 0x1:
+ if ((ins & 0x02000010) == 0x02000010) {
+ // Undefined!
+ return (pc+1);
+ } else {
+ if ((ins & 0x00100000) == 0) {
+ // STR
+ return (pc+1);
+ } else {
+ // LDR
+ if ((ins & 0x0000F000) != 0x0000F000) {
+ // Rd not PC
+ return (pc+1);
+ } else {
+ Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
+ if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
+ if (ins & 0x01000000) {
+ // Add/subtract offset before
+ if ((ins & 0x02000000) == 0) {
+ // Immediate offset
+ if (ins & 0x00800000) {
+ // Add offset
+ Rn += (ins & 0x00000FFF);
+ } else {
+ // Subtract offset
+ Rn -= (ins & 0x00000FFF);
+ }
+ } else {
+ // Offset is in a register
+ if (ins & 0x00800000) {
+ // Add offset
+ Rn += RmShifted(ins & 0x00000FFF);
+ } else {
+ // Subtract offset
+ Rn -= RmShifted(ins & 0x00000FFF);
+ }
+ }
+ }
+ return ((unsigned long *)*(unsigned long *)Rn);
+ }
+ }
+ }
+ return (pc+1);
+ case 0x2: // Branch, LDM/STM
+ if ((ins & 0x02000000) == 0) {
+ // LDM/STM
+ if ((ins & 0x00100000) == 0) {
+ // STM
+ return (pc+1);
+ } else {
+ // LDM
+ if ((ins & 0x00008000) == 0) {
+ // PC not in list
+ return (pc+1);
+ } else {
+ Rn = (unsigned long)get_register((ins & 0x000F0000) >> 16);
+ if ((ins & 0x000F0000) == 0x000F0000) Rn += 8; // PC prefetch!
+ offset = ins & 0x0000FFFF;
+ reg_count = 0;
+ for (i = 0; i < 15; i++) {
+ if (offset & (1<<i)) reg_count++;
+ }
+ if (ins & 0x00800000) {
+ // Add offset
+ Rn += reg_count*4;
+ } else {
+ // Subtract offset
+ Rn -= 4;
+ }
+ return ((unsigned long *)*(unsigned long *)Rn);
+ }
+ }
+ } else {
+ // Branch
+ if (ins_will_execute(ins)) {
+ offset = (ins & 0x00FFFFFF) << 2;
+ if (ins & 0x00800000) offset |= 0xFC000000; // sign extend
+ new_pc = (unsigned long)(pc+2) + offset;
+ // If its BLX, make new_pc a thumb address.
+ if ((ins & 0xFE000000) == 0xFA000000) {
+ if ((ins & 0x01000000) == 0x01000000)
+ new_pc |= 2;
+ new_pc = MAKE_THUMB_ADDR(new_pc);
+ }
+ return ((unsigned long *)new_pc);
+ } else {
+ // Falls through
+ return (pc+1);
+ }
+ }
+ case 0x3: // Coprocessor & SWI
+ if (((ins & 0x03000000) == 0x03000000) && ins_will_execute(ins)) {
+ // SWI
+ return (unsigned long *)(CYGNUM_HAL_VECTOR_SOFTWARE_INTERRUPT * 4);
+ } else {
+ return (pc+1);
+ }
+ default:
+ // Never reached - but fixes compiler warning.
+ return 0;
+ }
+}
+
+// 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, sp;
+ int i;
+
+ switch ((ins & 0xf000) >> 12) {
+ case 0x4:
+ // Check for BX or BLX
+ if ((ins & 0xff07) == 0x4700)
+ new_pc = (unsigned long)get_register((ins & 0x00078) >> 3);
+ break;
+ case 0xb:
+ // push/pop
+ // Look for "pop {...,pc}"
+ if ((ins & 0xf00) == 0xd00) {
+ // find PC
+ sp = (unsigned long)get_register(SP);
+
+ for (offset = i = 0; i < 8; i++)
+ if (ins & (1 << i))
+ offset += 4;
+
+ new_pc = *(cyg_uint32 *)(sp + offset);
+
+ if (!v5T_semantics())
+ new_pc = MAKE_THUMB_ADDR(new_pc);
+ }
+ break;
+ case 0xd:
+ // Bcc | SWI
+ // Use ARM function to check condition
+ arm_ins = ((unsigned long)(ins & 0x0f00)) << 20;
+ if ((arm_ins & 0xF0000000) == 0xF0000000) {
+ // SWI
+ new_pc = CYGNUM_HAL_VECTOR_SOFTWARE_INTERRUPT * 4;
+ } else 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/BLX (4byte instruction!)
+ // First instruction (bit 11 == 0) holds top-part of offset
+ if ((ins & 0x0800) == 0) {
+ 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);
+ // Check for BL/BLX
+ if ((ins & 0xE800) == 0xE800) {
+ offset |= (ins & 0x07ff) << 1;
+ new_pc = (unsigned long)(pc+4) + offset;
+ // If its BLX, force a full word alignment
+ // Otherwise, its a thumb address.
+ if (!(ins & 0x1000))
+ new_pc &= ~3;
+ else
+ new_pc = MAKE_THUMB_ADDR(new_pc);
+ }
+ }
+ 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
+ ss_saved_pc = (unsigned long) target_ins((unsigned long*)pc,
+ curins);
+ } else {
+ // The current instruction will not execute (the conditions
+ // 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;
+ }
+}
+
+ ****************************************************************************/
+
+
+/* _dbg_next_instruction_addr
+ * Determine the address of the next instruction to execute.
+ * On exit:
+ * R1: Instruction Address (31 bits, b0 = THUMB flag)
+ *
+ * Here we make use of the Debugger Stack which contains the address of the aborted instruction that will be reexecuted
+ * when we resume the program.
+ *
+ * If it is a Manual Breakpoint inserted into the code, then we will need to update the aborted instruction
+ * address to skip the current aborted instruction and resume execution at the next instruction address,
+ * and the next instruction address to be returned to the calling routine is the following instruction
+ * address instead.
+ *
+ * We need to check the aborted instruction type, to see if it is a branch instruction, before we can determine
+ * the next instruction address (for inserting a Breakpoint).
+ */
+_dbg_next_instruction_addr:
+/* We assume that any BKPT instructions in the code will be Manual Breakpoints,
+ * i.e., the Debugger does not leave stray Single Step / Auto / Normal breakpoints in memory
+ */
+
+ mov r2, #DBGSTACK_USERCPSR_INDEX /* Retrieve User CPSR */
+ _getdbgregisterfromindex r2, r0 /* Retrieve Register contents into R0 */
+ and r4, r0, #CPSR_THUMB /* store Thumb Mode status in R4 */
+ mov r5, r0, lsr #28 /* store CPSR condition flags in R5[3:0] */
+
+ _dbg_getabortedinstr_addr r2 /* Retrieve aborted instruction address */
+1: teq r4, #0 /* Check if it is ARM or Thumb instruction */
+ ldrneh r0, [r2]
+ ldrne r1, =(BKPT16_INSTR | BKPT16_MANUAL_BKPT) /* check for Thumb Manual Breakpoint Instruction */
+ ldreq r0, [r2]
+ ldreq r1, =(BKPT32_INSTR | BKPT32_MANUAL_BKPT) /* check for ARM Manual Breakpoint Instruction */
+ teq r0, r1
+ bne 2f /* Not Manual breakpoint */
+ teq r4, #0 /* Check if it is ARM or Thumb instruction */
+ addne r2, r2, #2 /* Is Manual Breakpoint, Skip to next Thumb instruction */
+ addeq r2, r2, #4 /* Is Manual Breakpoint, Skip to next ARM instruction */
+ _dbg_setabortedinstr_addr r2 /* Update aborted instruction address */
+ b 1b /* To protect against a sequence of Manual Breakpoint Instructions */
+
+/* Here, r0 contains the instruction which will be reexecuted when program resumes. We need to dissect it to see if
+ * it is a branch instruction.
+ * For ARM instructions, we also need to evaluate the current (breakpointed) instruction to see if it'll execute.
+ * If not, then the next instruction is the instruction following the current instruction.
+ */
+2:
+ /* Use R6 to store candidate next instruction address */
+ teq r4, #0 /* Check if it is ARM or Thumb instruction */
+ beq _next_instr_is_arm
+_next_instr_is_thumb:
+ add r6, r2, #2 /* set next Thumb instruction address */
+ /*_is_thumb_branch_instr r0 */ /* check if the current instruction is a branch instruction */
+_next_instr_is_arm:
+ add r6, r2, #4 /* Is ARM, set next ARM instruction address */
+@@@@@@@@@
+ bx lr
+
+/* __dbg__resume_execution
+ * cleanup, resume execution of program.
+ * Restore User Mode Regsiters from Debugger Stack, and resume execution from aborted instruction
+ */
+__dbg__resume_execution:
+@@@@@@
+ bl _dbg__flush_icache
+ b __dbg__resume_execution
+
+/****************************************************************************
+ *
+ * Instruction Decode Routines
+ *
+ ****************************************************************************/
+
+/* _dbg_check_arm_condcode
+ * Check ARM conditional execution code
+ * On entry:
+ * R0: instruction to be executed
+ * R5[3:0]: CPSR condition codes
+ * On exit:
+ * R0: will_execute (boolean)
+ */
+
+_dbg_check_arm_condcode:
+ stmfd sp!, {r6,lr} /* Use R6 as temporary will_execute variable */
+ mov r6, #TRUE
+ mov r0, r0, lsr #28 /* convert condition code to index (0-F) */
+ ldr r2, =debug_armCondCodeTable
+ ldrb r1, [r2, r0] /* Get condition code mask */
+/*
+ * The following check is unnecessary as it is covered by the set/clear checking algorithm
+ teq r1, #0
+ beq _dbg_check_arm_condcode_exit
+*/
+ teq r1, #0xFF
+ bne _dbg_check_bits_set
+
+
+/*
+ * Complex Checks:
+ *
+ * will_execute = TRUE [default condition]
+ * If (N == V) bit set
+ * will_execute = (N == V)
+ * else
+ * will_execute = (N != V)
+ *
+ * If (ANDOR bit) set
+ * z_cond = ((Z XOR Z set) == 0)
+ * If (AND bit set)
+ * will_execute = will_execute && z_cond
+ * else
+ * will_execute = will_execute || z_cond
+ */
+_dbg_cond_complex_check:
+ sub r1, r0, #COMPLEX_CONDCODE_START /* Convert complex condition code to new index (0-3) */
+ ldr r2, =debug_armComplexCCTable
+ ldrb r1, [r2, r1] /* Get complex condition code bitmap */
+
+ /* Use r2 to store N, r3 to store V */
+ tst r5, #COMPLEX_CONDCODE_NFLAG
+ moveq r2, #FALSE
+ movne r2, #TRUE /* r2 = N flag */
+ tst r5, #COMPLEX_CONDCODE_VFLAG
+ moveq r3, #FALSE
+ movne r3, #TRUE /* r3 = V flag */
+ eor r2, r2, r3 /* r2 = (N xor V): 0 if equal, 0xFF if not equal */
+ tst r1, #COMPLEX_CONDCODE_NEQV_MASK
+ mvnne r6, r1 /* If (N == V) bit set, will_execute (r6) = TRUE if (N == V) [r2 == 0] -> invert r2 */
+ moveq r6, r1 /* else (N == V) bit clr, will_execute (r6) = TRUE if (N != V) [r2 == 0xFF] */
+
+ tst r1, #COMPLEX_CONDCODE_ANDOR_MASK
+ beq _dbg_check_arm_condcode_exit /* No additional checks needed, exit */
+
+ /* Use r2 to store Z, r3 to store Z set */
+ and r2, r5, #COMPLEX_CONDCODE_ZFLAG /* r2 = Z flag */
+ and r3, r1, #COMPLEX_CONDCODE_ZSET_MASK /* r3 = Z set */
+ eors r2, r2, r3 /* r2 = (Z xor Z set): 0 if matched, non-zero if not matched */
+ moveq r2, #TRUE
+ movne r2, #FALSE /* r2 (z_cond): TRUE if matched, FALSE if not matched */
+
+ tst r1, #COMPLEX_CONDCODE_AND_MASK
+ andne r6, r6, r2 /* If AND bit set, will_execute = will_execute && z_cond */
+ orreq r6, r6, r2 /* else, will_execute = will_execute || z_cond */
+ b _dbg_check_arm_condcode_exit
+
+
+/*
+ * Simple Checks:
+ *
+ * will_execute = TRUE [default condition, equivalent to 0x00 (AL) ]
+ * If (SetBitMask is Non-Zero)
+ * will_execute = ((cond_code & SetBitMask) == SetBitMask)
+ * If will_execute && (ClearBitMask is Non-Zero)
+ * will_execute = will_execute && ((cond_code | ~ClearBitMask) == ~ClearBitMask)
+ */
+
+_dbg_check_bits_set:
+ movs r0, r1, lsr #4 /* R0: bits set */
+ beq _dbg_check_bits_clear
+ and r2, r5, r0 /* Check bits set IF bitmask non-zero */
+ teq r2, r0 /* ((cond_code & SetBitMask) == SetBitMask)? */
+ movne r6, #FALSE /* No, so will_execute = FALSE */
+ bne _dbg_check_arm_condcode_exit
+
+_dbg_check_bits_clear:
+ ands r1, r1, #NIBBLE0 /* R1: bits clear */
+ beq _dbg_check_arm_condcode_exit
+ mvn r1, r1 /* Invert Bitmask */
+ orr r2, r5, r1 /* Check bits clear IF bitmask non-zero */
+ teq r2, r1 /* ((cond_code | ~ClearBitMask) == ~ClearBitMask)? */
+ movne r6, #FALSE /* No, so will_execute = FALSE */
+ bne _dbg_check_arm_condcode_exit
+
+
+_dbg_check_arm_condcode_exit:
+ mov r0, r6 /* Update return value */
+ ldmfd sp!, {r6, pc}
+
+
+_arm_data_instr_handler: /* Data Processing instr with Rd = R15 */
+_arm_bx_blx_handler: /* BX or BLX */
+_arm_ldr_pc_handler: /* LDR with Rd = PC */
+_arm_ldm_pc_handler: /* LDM {pc} */
+_arm_b_bl_handler: /* B or BL. Note v4t does not have BLX instr */
+_arm_coproc_swi_handler: /* Coprocessor instr or SWI */
+ bx lr
+
+_thumb_bx_blx_handler: /* BX or BLX. Note: b7 (H1) is not matched in the mask */
+_thumb_poppc_handler: /* PUSH/POP, specifically POP {Rlist,PC} */
+_thumb_bcond_swi_handler: /* B<cond> or SWI */
+_thumb_b_handler: /* B */
+_thumb_long_b_handler: /* Long BL or BLX (4 bytes) Note: b11 (H) indicates 1st or 2nd instr */
+ bx lr
+
+
+/****************************************************************************
+ *
+ * Breakpoint Manipulation Routines
+ *
+ ****************************************************************************/
+/* _dbg_find_breakpoint_slot
+ * Find the matching Breakpoint Slot.
+ * This is both used to find empty slots (pass R0=0x0000) or
+ * occupied slots (pass R0=<brkpt addr>)
+ *
+ * On Entry:
+ * R0: Breakpoint Address
+ * On Exit:
+ * R0: Matching Index (-1: not found)
+ *
+ * NOTE: This routine performs exact match, i.e., breakpoint address MUST be configured
+ * for ARM or Thumb (bit 0 clear/set) as appropriate
+ */
+
+_dbg_find_breakpoint_slot:
+ stmfd sp!, {r1,r2,r3, lr}
+ mov r1, #1 /* Only consider Breakpoints 1-7 */
+ ldr r3, =__breakpoints_num__
+1:
+ _index2bkptindex_addr r1, r2 /* Calculate Breakpoint Entry Address */
+ ldr r2, [r2] /* Get actual breakpoint entry (instruction address) */
+ cmp r0, r2
+ beq _found_breakpoint_slot
+ add r1, r1, #1 /* no match, check next */
+ cmp r1, r3
+ blo 1b /* continue checking only if we don't exceed __breakpoints_num__ */
+
+_notfound_breakpoint_slot:
+ mov r1, #CMD_BKPT_NOTFOUND
+_found_breakpoint_slot:
+ mov r0, r1 /* Return value in R0 */
+ ldmfd sp!, {r1,r2,r3, pc}
+
+/* _dbg__clear_singlestep
+ * Clear the Single Step Breakpoint
+ */
+_dbg__clear_singlestep:
+ ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */
+/* b _dbg__clear_one_breakpoint */
+
+/* _dbg__clear_one_breakpoint
+ * On entry, R0 contains the Breakpoint State slot address to be cleared
+ *
+ */
+_dbg__clear_one_breakpoint:
+ mov r1, #0
+ mov r2, #0
+ stmea r0!, {r1, r2} /* clear Breakpoint state */
+ bx lr
+
+/* _dbg__clear_breakpoints
+ * Routine iterates through the array of breakpoints (incl single step breakpoint) and clears the breakpoint
+ */
+_dbg__clear_breakpoints:
+ stmfd sp!, {lr}
+ ldr r0, =__breakpoints_start__ /* Single Step Breakpoint is at the beginning of the Breakpoint State Struct */
+ ldr r3, =__breakpoints_end__ /* start from top of the table */
+3: bl _dbg__clear_one_breakpoint
+ cmp r0, r3
+ blo 3b
+ ldmfd sp!, {pc}
+
+/* _dbg__install_singlestep
+ * Install the Single Step Breakpoint
+ * On entry:
+ * R1: Instruction Address (31 bits, b0 = THUMB flag)
+ */
+_dbg__install_singlestep:
+ mov r0, #0
+/* b _dbg__install_one_breakpoint */
+
+/* _dbg__install_one_breakpoint
+ * Install breakpoint entry into Breakpoint State Table
+ * On entry:
+ * R0: Breakpoint index (assumed valid)
+ * R1: Instruction Address (31 bits, b0 = THUMB flag)
+ *
+ * On exit:
+ * R2: Breakpoint Instruction
+ * R3: Breakpoint Entry address
+ */
+_dbg__install_one_breakpoint:
+/* Check for Thumb bit */
+ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */
+/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */
+ ldreq r2, [r1] /* if 0: load ARM instruction from address location */
+ ldrneh r2, [r1] /* else load Thumb instruction */
+ _index2bkptindex_addr r0, r3 /* Calculate Breakpoint Entry Address */
+ stm r3, {r1, r2}
+ bx lr
+
+
+/* _dbg__restore_singlestep
+ * Restores the contents of the single step breakpoint to memory
+ */
+_dbg__restore_singlestep:
+ mov r0, #0 /* single step breakpoint index */
+ _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */
+ ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */
+ teq r1, #0
+ bxeq lr /* Exit if not active */
+/* b _dbg__restore_one_breakpoint */
+
+/* _dbg__restore_one_breakpoint
+ * Restores the contents to memory for one breakpoint
+ * On entry:
+ * R0: Breakpoint index (assumed valid) [not used -- can be used for validating BKPT]
+ * R1: Breakpoint Address (assumed valid)
+ * R2: Breakpoint Instruction (assumed valid)
+ */
+_dbg__restore_one_breakpoint:
+/* Check for Thumb bit */
+ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */
+/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */
+ streq r2, [r1] /* if 0: restore ARM instruction to address location */
+ bicne r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */
+ strneh r2, [r1] /* store Thumb instruction */
+ bx lr
+
+/* _dbg__restore_breakpoints
+ * Routine iterates through the array of breakpoints (incl single step breakpoint) and restores the contents to memory
+ * Only Active breakpoints (i.e., Non-zero Address) are processed.
+ */
+_dbg__restore_breakpoints:
+ stmfd sp!, {lr}
+ ldr r5, =_dbg__restore_one_breakpoint
+ b __dbg__iterate_breakpoint_array
+
+/* _dbg__activate_singlestep
+ * Activate the single step breakpoint to memory
+ */
+_dbg__activate_singlestep:
+ mov r0, #0 /* single step breakpoint index */
+ _index2bkptindex_addr r0, r1 /* Calculate Single Step Breakpoint Entry Address */
+ ldm r1, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */
+ teq r1, #0
+ bxeq lr /* Exit if not active */
+/* b _dbg__activate_one_breakpoint */
+
+/* _dbg__activate_one_breakpoint
+ * Activate one breakpoint to memory
+ * On entry:
+ * R0: Breakpoint index (assumed valid)
+ * R1: Breakpoint Address (assumed valid)
+ * R2: Breakpoint Instruction (assumed valid)
+ */
+_dbg__activate_one_breakpoint:
+/* Check for Thumb bit */
+ tst r1, #BKPT_STATE_THUMB_FLAG /* 1: Thumb instruction */
+ bne _nx_is_thumb_bp
+_nx_is_arm_bp:
+/* Assume that the address entry is valid, otherwise we should sanitize it (mask out b1) */
+ ldr r3, [r1] /* if 0: load ARM instruction from address location */
+ teq r2, r3 /* check that the two instructions are identical */
+ bne _dbg__breakpoint_invalid_arm
+ ldr r2, =BKPT32_INSTR /* ARM BKPT instruction */
+ and r2, r2, r0 /* Merge Breakpoint index */
+ str r2, [r1] /* Store it into memory location */
+_dbg__breakpoint_invalid_arm:
+ bx lr
+_nx_is_thumb_bp:
+ bic r1, #BKPT_STATE_THUMB_FLAG /* else, clear Thumb Flag */
+ ldrh r3, [r1] /* load Thumb instruction from address location */
+ teq r2, r3 /* check that the two instructions are identical */
+ bne _dbg__breakpoint_invalid_thumb
+ ldr r2, =BKPT16_INSTR /* Thumb BKPT instruction */
+ and r2, r2, r0 /* Merge Breakpoint index */
+ strh r2, [r1] /* Store it into memory location */
+_dbg__breakpoint_invalid_thumb:
+ bx lr
+
+/* _dbg__activate_breakpoints
+ * Routine iterates through the array of breakpoints (incl single step breakpoint) and activates them
+ * Only Active breakpoints (i.e., Non-zero Address) are processed.
+ */
+_dbg__activate_breakpoints:
+ stmfd sp!, {lr}
+ ldr r5, =_dbg__activate_one_breakpoint
+ b __dbg__iterate_breakpoint_array
+
+
+/* __dbg__iterate_breakpoint_array
+ * Common routine iterates through the array of breakpoints (incl single step breakpoint)
+ * and executes routine given in R5, passing:
+ * R0: Breakpoint index
+ * R1: Breakpoint Address
+ * R2: Breakpoint Instruction
+ *
+ * On Entry:
+ * Assumes that lr has been push to stack (routine can't be called directly)
+ *
+ * Only Active breakpoints (i.e., Non-zero Address entries) are processed.
+ */
+__dbg__iterate_breakpoint_array:
+ ldr r4, =__breakpoints_end__ /* start from top of the table (Assume __breakpoints_end__ > __breakpoints_start__) */
+ ldr r3, =__breakpoints_start__ /* end address check */
+ ldr r0, =__breakpoints_num__ /* Number of Breakpoints (incl Single Step) (Assume Non-Zero) */
+4: sub r0, r0, #1 /* Decrement breakpoint index in r0 */
+ ldmea r4!, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */
+ teq r1, #0 /* Is it active? */
+ movne lr, pc
+ bxne r5 /* active entry */
+ cmp r4, r3
+ bhi 4b /* if (pointer > start of Breakpoint Table address), get next slot */
+ ldmfd sp!, {pc}
+
+/* _dbg__activate_autobreakpoint
+ * Activate all other breakpoints except current breakpoint, activate auto breakpoint in next instr slot
+ * On entry:
+ * R0: Current Breakpoint index (assumed valid)
+ * R1: Next Instruction address (for AUTO Breakpoint) [Not used, assume Single Step Breakpoint already has correct info]
+ */
+_dbg__activate_autobreakpoint:
+ stmfd sp!, {lr}
+ mov r5, r0 /* Keep Current Breakpoint Index in r5 */
+ ldr r4, =__breakpoints_end__ /* start from top of the table */
+ ldr r0, =__breakpoints_num__ /* Number of Breakpoints (incl Single Step) (Assume Non-Zero) */
+4: subs r0, r0, #1 /* Decrement breakpoint index in r0 */
+ ldmea r4!, {r1, r2} /* r1: Breakpoint Address, r2: Breakpoint Instruction */
+ bls 5f /* Flag set by subs instruction previously. Reached Single Step, go activate AUTO Breakpoint */
+ teq r0, r5 /* Is it the Current Breakpoint? */
+ beq 4b /* Yes, so skip */
+ teq r1, #0 /* Is it active? */
+ blne _dbg__activate_one_breakpoint /* active entry */
+ b 4b /* Next iteration */
+5:
+/* Here, r1: Breakpoint Address, r2: Breakpoint Instruction */
+ tst r1, #BKPT_STATE_THUMB_FLAG /* Check for Thumb bit -- 1: Thumb instruction */
+ orreq r0, r5, #BKPT32_AUTO_BKPT /* Is ARM Instruction, merge AUTO flag with Current Breakpoint Index */
+ orrne r0, r5, #BKPT16_AUTO_BKPT /* Is Thumb Instruction, merge AUTO flag with Current Breakpoint Index */
+ bl _dbg__activate_one_breakpoint /* Activate AUTO Breakpoint */
+ ldmfd sp!, {pc}
+