diff --git a/src/target/armv7m.c b/src/target/armv7m.c
index fe8bc3ba9140160acb6726daa72255009b54cebc..a2e74702160a99dc7b18c27edb237ca24435fa9c 100644
--- a/src/target/armv7m.c
+++ b/src/target/armv7m.c
@@ -25,6 +25,10 @@
  *   along with this program; if not, write to the                         *
  *   Free Software Foundation, Inc.,                                       *
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ *                                                                         *
+ *	ARMv7-M Architecture, Application Level Reference Manual           *
+ *              ARM DDI 0405C (September 2008)                             *
+ *                                                                         *
  ***************************************************************************/
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -44,8 +48,10 @@ char* armv7m_mode_strings[] =
 
 char* armv7m_exception_strings[] =
 {
-	"", "Reset", "NMI", "HardFault", "MemManage", "BusFault", "UsageFault", "RESERVED", "RESERVED", "RESERVED", "RESERVED",
-	"SVCall", "DebugMonitor", "RESERVED", "PendSV", "SysTick"
+	"", "Reset", "NMI", "HardFault",
+	"MemManage", "BusFault", "UsageFault", "RESERVED",
+	"RESERVED", "RESERVED", "RESERVED", "SVCall",
+	"DebugMonitor", "RESERVED", "PendSV", "SysTick"
 };
 
 char* armv7m_core_reg_list[] =
@@ -54,8 +60,8 @@ char* armv7m_core_reg_list[] =
 	"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12",
 	"sp", "lr", "pc",
 	"xPSR", "msp", "psp",
-	/* Registers accessed through special reg 20 */
-	"primask", "basepri", "faultmask", "control"
+	/* reg 20 has 4 bytes: CONTROL, FAULTMASK, BASEPRI, PRIMASK */
+	"spec20",
 };
 
 uint8_t armv7m_gdb_dummy_fp_value[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
@@ -105,11 +111,11 @@ armv7m_core_reg_t armv7m_core_reg_list_arch_info[] =
 	{17, ARMV7M_REGISTER_CORE_GP, ARMV7M_MODE_ANY, NULL, NULL}, /* MSP */
 	{18, ARMV7M_REGISTER_CORE_GP, ARMV7M_MODE_ANY, NULL, NULL}, /* PSP */
 
-	/*  CORE_SP are accesible using coreregister 20 */
-	{19, ARMV7M_REGISTER_CORE_SP, ARMV7M_MODE_ANY, NULL, NULL}, /* PRIMASK */
-	{20, ARMV7M_REGISTER_CORE_SP, ARMV7M_MODE_ANY, NULL, NULL}, /* BASEPRI */
-	{21, ARMV7M_REGISTER_CORE_SP, ARMV7M_MODE_ANY, NULL, NULL}, /* FAULTMASK */
-	{22, ARMV7M_REGISTER_CORE_SP, ARMV7M_MODE_ANY, NULL, NULL}  /* CONTROL */
+	/* FIXME the register numbers here are core-specific.
+	 * Numbers 0..18 above work for all Cortex-M3 revisions.
+	 * Number 20 below works for CM3 r2p0 and later.
+	 */
+	{20, ARMV7M_REGISTER_CORE_SP, ARMV7M_MODE_ANY, NULL, NULL},
 };
 
 int armv7m_core_reg_arch_type = -1;
@@ -381,12 +387,21 @@ int armv7m_run_algorithm(struct target_s *target, int num_mem_params, mem_param_
 		armv7m_set_core_reg(reg, reg_params[i].value);
 	}
 
-	if (armv7m_algorithm_info->core_mode != ARMV7M_MODE_ANY)
+	/* NOTE:  CONTROL is bits 31:24 of SPEC20 register, if it's present;
+	 * holding a two-bit field.
+	 *
+	 * FIXME need a solution using ARMV7M_T_MSR().  Use it at least for
+	 * earlier cores.
+	 */
+	if (armv7m_algorithm_info->core_mode != ARMV7M_MODE_ANY
+			&& armv7m->has_spec20)
 	{
 		LOG_DEBUG("setting core_mode: 0x%2.2x", armv7m_algorithm_info->core_mode);
-		buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 1, armv7m_algorithm_info->core_mode);
-		armv7m->core_cache->reg_list[ARMV7M_CONTROL].dirty = 1;
-		armv7m->core_cache->reg_list[ARMV7M_CONTROL].valid = 1;
+
+		buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_SPEC20].value,
+				24, 2, armv7m_algorithm_info->core_mode);
+		armv7m->core_cache->reg_list[ARMV7M_SPEC20].dirty = 1;
+		armv7m->core_cache->reg_list[ARMV7M_SPEC20].valid = 1;
 	}
 
 	/* ARMV7M always runs in Thumb state */
diff --git a/src/target/armv7m.h b/src/target/armv7m.h
index ec90b7150d0d8a16ab31724970156fb6922f5912..17e3ff382f05b9bc0fafddb2ba6ace5ea097cf81 100644
--- a/src/target/armv7m.h
+++ b/src/target/armv7m.h
@@ -61,10 +61,15 @@ enum
 	ARMV7M_xPSR = 16,
 	ARMV7M_MSP,
 	ARMV7M_PSP,
-	ARMV7M_PRIMASK,
-	ARMV7M_BASEPRI,
-	ARMV7M_FAULTMASK,
-	ARMV7M_CONTROL,
+
+	/* FIXME the register numbers here are core-specific.  Cortex-M3
+	 * through r1p1 only defines registers up to PSP; see ARM DDI 0337E.
+	 *
+	 * It's r2p0 (see ARM DDI 0337G) which defines the register that's
+	 * called SPEC20 here, with four single-byte fields with CONTROL
+	 * (highest byte), FAULTMASK, BASEPRI, and PRIMASK (lowest byte).
+	 */
+	ARMV7M_SPEC20 = 20,
 	ARMV7NUMCOREREGS
 };
 
@@ -78,6 +83,7 @@ typedef struct armv7m_common_s
 	int exception_number;
 	swjdp_common_t swjdp_info;
 
+	bool has_spec20;
 
 	/* Direct processor core register read and writes */
 	int (*load_core_reg_u32)(struct target_s *target, enum armv7m_regtype type, uint32_t num, uint32_t *value);
diff --git a/src/target/cortex_m3.c b/src/target/cortex_m3.c
index 2e19cd6781c649eac83e30b2dae1282ca49b822d..8cb1bd4e7a69c5dd09527500cc48e4fcb44436fe 100644
--- a/src/target/cortex_m3.c
+++ b/src/target/cortex_m3.c
@@ -24,7 +24,7 @@
  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
  *                                                                         *
  *                                                                         *
- *   Cortex-M3(tm) TRM, ARM DDI 0337C                                      *
+ *   Cortex-M3(tm) TRM, ARM DDI 0337E (r1p1) and 0337G (r2p0)              *
  *                                                                         *
  ***************************************************************************/
 #ifdef HAVE_CONFIG_H
@@ -395,7 +395,7 @@ int cortex_m3_debug_entry(target_t *target)
 
 	/* Examine target state and mode */
 	/* First load register acessible through core debug port*/
-	for (i = 0; i < ARMV7M_PRIMASK; i++)
+	for (i = 0; i < ARMV7NUMCOREREGS; i++)
 	{
 		if (!armv7m->core_cache->reg_list[i].valid)
 			armv7m->read_core_reg(target, i);
@@ -418,22 +418,19 @@ int cortex_m3_debug_entry(target_t *target)
 		cortex_m3_store_core_reg_u32(target, ARMV7M_REGISTER_CORE_GP, 16, xPSR &~ 0xff);
 	}
 
-	/* Now we can load SP core registers */
-	for (i = ARMV7M_PRIMASK; i < ARMV7NUMCOREREGS; i++)
-	{
-		if (!armv7m->core_cache->reg_list[i].valid)
-			armv7m->read_core_reg(target, i);
-	}
-
 	/* Are we in an exception handler */
 	if (xPSR & 0x1FF)
 	{
 		armv7m->core_mode = ARMV7M_MODE_HANDLER;
 		armv7m->exception_number = (xPSR & 0x1FF);
 	}
-	else
+	else if (armv7m->has_spec20)
 	{
-		armv7m->core_mode = buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_CONTROL].value, 0, 1);
+		/* NOTE:  CONTROL is bits 31:24 of SPEC20 register, holding
+		 * a two-bit field.  Unavailable before r2p0...
+		 */
+		armv7m->core_mode = buf_get_u32(
+			armv7m->core_cache->reg_list[ARMV7M_SPEC20].value, 24, 2);
 		armv7m->exception_number = 0;
 	}
 
@@ -636,16 +633,26 @@ int cortex_m3_resume(struct target_s *target, int current, uint32_t address, int
 	if (debug_execution)
 	{
 		/* Disable interrupts */
-		/* We disable interrupts in the PRIMASK register instead of masking with C_MASKINTS,
+		/* We disable interrupts in the PRIMASK register instead
+		 * of masking with C_MASKINTS,
 		 * This is probably the same issue as Cortex-M3 Errata	377493:
-		 * C_MASKINTS in parallel with disabled interrupts can cause local faults to not be taken. */
-		buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_PRIMASK].value, 0, 32, 1);
-		armv7m->core_cache->reg_list[ARMV7M_PRIMASK].dirty = 1;
-		armv7m->core_cache->reg_list[ARMV7M_PRIMASK].valid = 1;
+		 * C_MASKINTS in parallel with disabled interrupts can cause
+		 * local faults to not be taken.  (FIXED in r1p0 and later.)
+		 *
+		 * NOTE:  PRIMASK is bits 7:0 of SPEC20 register, holding a
+		 * one bit field.  Available this way for r2p0 and later...
+		 */
+		if (armv7m->has_spec20) {
+			buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_SPEC20]
+					.value, 0, 1, 1);
+			armv7m->core_cache->reg_list[ARMV7M_SPEC20].dirty = 1;
+			armv7m->core_cache->reg_list[ARMV7M_SPEC20].valid = 1;
+		}
 
 		/* Make sure we are in Thumb mode */
 		buf_set_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32,
-			buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32) | (1 << 24));
+			buf_get_u32(armv7m->core_cache->reg_list[ARMV7M_xPSR].value, 0, 32)
+			| (1 << 24));
 		armv7m->core_cache->reg_list[ARMV7M_xPSR].dirty = 1;
 		armv7m->core_cache->reg_list[ARMV7M_xPSR].valid = 1;
 	}
@@ -1467,8 +1474,14 @@ int cortex_m3_examine(struct target_s *target)
 		if ((retval = target_read_u32(target, CPUID, &cpuid)) != ERROR_OK)
 			return retval;
 
-		if (((cpuid >> 4) & 0xc3f) == 0xc23)
+		if (((cpuid >> 4) & 0xc3f) == 0xc23) {
 			LOG_DEBUG("CORTEX-M3 processor detected");
+			if (((cpuid >> 20) & 0xf) >= 2) {
+				armv7m->has_spec20 = true;
+				LOG_DEBUG("r2p0 or later detected");
+			}
+		} else
+			LOG_WARNING("not a CORTEX-M3 processor?");
 		LOG_DEBUG("cpuid: 0x%8.8" PRIx32 "", cpuid);
 
 		target_read_u32(target, NVIC_ICTR, &ictr);
diff --git a/src/target/cortex_m3.h b/src/target/cortex_m3.h
index e6714d0077fde554af5453a4692f3e5cc167cbc8..ba61b1cba45ab08f1f44e4b86dd25a1ccf93df2e 100644
--- a/src/target/cortex_m3.h
+++ b/src/target/cortex_m3.h
@@ -117,6 +117,21 @@ extern char* cortex_m3_state_strings[];
 #define FPCR_REPLACE_BKPT_HIGH  (2 << 30)
 #define FPCR_REPLACE_BKPT_BOTH  (3 << 30)
 
+/* Use MRS/MSR to read/write any of these special registers.  Some of
+ * them (xPSR, MSP, PSP) are always available using DCRxR.  Starting in
+ * Cortex-M3 r2p0, some (CONTROL, BASEPRI, and *MASK) are also available
+ * through DCRxR.
+ * NOTE: this listing omits xPSR components and other mixtures.
+ */
+#define SR_XPSR		3
+#define SR_MSP		8
+#define SR_PSP		9
+#define SR_PRIMASK	16
+#define SR_BASEPRI	17
+#define SR_BASEPRI_MAX	18
+#define SR_FAULTMASK	19
+#define SR_CONTROL	20
+
 typedef struct  cortex_m3_fp_comparator_s
 {
 	int used;