From 8b89224c6e206dcfa2d8bae10dcfb8251b3cb333 Mon Sep 17 00:00:00 2001
From: zwelch <zwelch@b42882b7-edfa-0310-969c-e2dbd0fdcd60>
Date: Wed, 15 Jul 2009 23:48:11 +0000
Subject: David Brownell <david-b@pacbell.net>:

Make the Thumb2 disassembler handle a bunch of 32-bit instructions:

  A5.3.4 Branches and miscellaneous control

Note that this shifts some responsabililty out of helper functions,
making the code and layout simpler for 32-bit decoders:  they only
need to know how to format the instruction and its parameters.

Also, technical note:  with this patch, Thumb1 decoders could now
call the Thumb2 decoder if they wanted to get nicer treatment of
the exiting 32-bit B/BLX instructions.


git-svn-id: svn://svn.berlios.de/openocd/trunk@2532 b42882b7-edfa-0310-969c-e2dbd0fdcd60
---
 src/target/arm_disassembler.c | 267 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 263 insertions(+), 4 deletions(-)

(limited to 'src/target')

diff --git a/src/target/arm_disassembler.c b/src/target/arm_disassembler.c
index 4d134213..69d5de87 100644
--- a/src/target/arm_disassembler.c
+++ b/src/target/arm_disassembler.c
@@ -2292,6 +2292,247 @@ int thumb_evaluate_opcode(uint16_t opcode, uint32_t address, arm_instruction_t *
 	return -1;
 }
 
+static int t2ev_b_bl(uint32_t opcode, uint32_t address,
+		arm_instruction_t *instruction, char *cp)
+{
+	unsigned offset;
+	unsigned b21 = 1 << 21;
+	unsigned b22 = 1 << 22;
+
+	/* instead of combining two smaller 16-bit branch instructions,
+	 * Thumb2 uses only one larger 32-bit instruction.
+	 */
+	offset = opcode & 0x7ff;
+	offset |= (opcode & 0x03ff0000) >> 5;
+	if (opcode & (1 << 26)) {
+		offset |= 0xff << 23;
+		if ((opcode & (1 << 11)) == 0)
+			b21 = 0;
+		if ((opcode & (1 << 13)) == 0)
+			b22 = 0;
+	} else {
+		if (opcode & (1 << 11))
+			b21 = 0;
+		if (opcode & (1 << 13))
+			b22 = 0;
+	}
+	offset |= b21;
+	offset |= b22;
+
+
+	address += 4;
+	address += offset << 1;
+
+	instruction->type = (opcode & (1 << 14)) ? ARM_BL : ARM_B;
+	instruction->info.b_bl_bx_blx.reg_operand = -1;
+	instruction->info.b_bl_bx_blx.target_address = address;
+	sprintf(cp, "%s\t%#8.8" PRIx32,
+			(opcode & (1 << 14)) ? "BL" : "B.W",
+			address);
+
+	return ERROR_OK;
+}
+
+static int t2ev_cond_b(uint32_t opcode, uint32_t address,
+		arm_instruction_t *instruction, char *cp)
+{
+	unsigned offset;
+	unsigned b17 = 1 << 17;
+	unsigned b18 = 1 << 18;
+	unsigned cond = (opcode >> 22) & 0x0f;
+
+	offset = opcode & 0x7ff;
+	offset |= (opcode & 0x003f0000) >> 5;
+	if (opcode & (1 << 26)) {
+		offset |= 0xffff << 19;
+		if ((opcode & (1 << 11)) == 0)
+			b17 = 0;
+		if ((opcode & (1 << 13)) == 0)
+			b18 = 0;
+	} else {
+		if (opcode & (1 << 11))
+			b17 = 0;
+		if (opcode & (1 << 13))
+			b18 = 0;
+	}
+	offset |= b17;
+	offset |= b18;
+
+	address += 4;
+	address += offset << 1;
+
+	instruction->type = ARM_B;
+	instruction->info.b_bl_bx_blx.reg_operand = -1;
+	instruction->info.b_bl_bx_blx.target_address = address;
+	sprintf(cp, "B%s.W\t%#8.8" PRIx32,
+			arm_condition_strings[cond],
+			address);
+
+	return ERROR_OK;
+}
+
+static const char *special_name(int number)
+{
+	char *special = "(RESERVED)";
+
+	switch (number) {
+	case 0:
+		special = "apsr";
+		break;
+	case 1:
+		special = "iapsr";
+		break;
+	case 2:
+		special = "eapsr";
+		break;
+	case 3:
+		special = "xpsr";
+		break;
+	case 5:
+		special = "ipsr";
+		break;
+	case 6:
+		special = "epsr";
+		break;
+	case 7:
+		special = "iepsr";
+		break;
+	case 8:
+		special = "msp";
+		break;
+	case 9:
+		special = "psp";
+		break;
+	case 16:
+		special = "primask";
+		break;
+	case 17:
+		special = "basepri";
+		break;
+	case 18:
+		special = "basepri_max";
+		break;
+	case 19:
+		special = "faultmask";
+		break;
+	case 20:
+		special = "control";
+		break;
+	}
+	return special;
+}
+
+static int t2ev_hint(uint32_t opcode, uint32_t address,
+		arm_instruction_t *instruction, char *cp)
+{
+	const char *mnemonic;
+
+	if (opcode & 0x0700) {
+		instruction->type = ARM_UNDEFINED_INSTRUCTION;
+		strcpy(cp, "UNDEFINED");
+		return ERROR_OK;
+	}
+
+	if (opcode & 0x00f0) {
+		sprintf(cp, "DBG\t#%d", opcode & 0xf);
+		return ERROR_OK;
+	}
+
+	switch (opcode & 0x0f) {
+	case 0:
+		mnemonic = "NOP.W";
+		break;
+	case 1:
+		mnemonic = "YIELD.W";
+		break;
+	case 2:
+		mnemonic = "WFE.W";
+		break;
+	case 3:
+		mnemonic = "WFI.W";
+		break;
+	case 4:
+		mnemonic = "SEV.W";
+		break;
+	default:
+		mnemonic = "HINT.W (UNRECOGNIZED)";
+		break;
+	}
+	strcpy(cp, mnemonic);
+	return ERROR_OK;
+}
+
+static int t2ev_misc(uint32_t opcode, uint32_t address,
+		arm_instruction_t *instruction, char *cp)
+{
+	const char *mnemonic;
+
+	switch ((opcode >> 4) & 0x0f) {
+	case 2:
+		mnemonic = "CLREX";
+		break;
+	case 4:
+		mnemonic = "DSB";
+		break;
+	case 5:
+		mnemonic = "DMB";
+		break;
+	case 6:
+		mnemonic = "ISB";
+		break;
+	default:
+		return ERROR_INVALID_ARGUMENTS;
+	}
+	strcpy(cp, mnemonic);
+	return ERROR_OK;
+}
+
+static int t2ev_b_misc(uint32_t opcode, uint32_t address,
+		arm_instruction_t *instruction, char *cp)
+{
+	/* permanently undefined */
+	if ((opcode & 0x07f07000) == 0x07f02000) {
+		instruction->type = ARM_UNDEFINED_INSTRUCTION;
+		strcpy(cp, "UNDEFINED");
+		return ERROR_OK;
+	}
+
+	switch ((opcode >> 12) & 0x5) {
+	case 0x1:
+	case 0x5:
+		return t2ev_b_bl(opcode, address, instruction, cp);
+	case 0x4:
+		goto undef;
+	case 0:
+		if (((opcode >> 23) & 0x07) == 0x07)
+			return t2ev_cond_b(opcode, address, instruction, cp);
+		if (opcode & (1 << 26))
+			goto undef;
+		break;
+	}
+
+	switch ((opcode >> 20) & 0x7f) {
+	case 0x38:
+	case 0x39:
+		sprintf(cp, "MSR\t%s, r%d", special_name(opcode & 0xff),
+				(opcode >> 16) & 0x0f);
+		return ERROR_OK;
+	case 0x3a:
+		return t2ev_hint(opcode, address, instruction, cp);
+	case 0x3b:
+		return t2ev_misc(opcode, address, instruction, cp);
+	case 0x3e:
+	case 0x3f:
+		sprintf(cp, "MRS\tr%d, %s", (opcode >> 16) & 0x0f,
+				special_name(opcode & 0xff));
+		return ERROR_OK;
+	}
+
+undef:
+	return ERROR_INVALID_ARGUMENTS;
+}
+
+
 /*
  * REVISIT for Thumb2 instructions, instruction->type and friends aren't
  * always set.  That means eventual arm_simulate_step() support for Thumb2
@@ -2302,6 +2543,7 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc
 	int retval;
 	uint16_t op;
 	uint32_t opcode;
+	char *cp;
 
 	/* clear low bit ... it's set on function pointers */
 	address &= ~1;
@@ -2332,13 +2574,30 @@ int thumb2_opcode(target_t *target, uint32_t address, arm_instruction_t *instruc
 		return thumb_evaluate_opcode(op, address, instruction);
 	}
 
-	/* FIXME decode the 32-bit instructions */
+	snprintf(instruction->text, 128,
+			"0x%8.8" PRIx32 "  0x%8.8" PRIx32 "\t",
+			address, opcode);
+	cp = strchr(instruction->text, 0);
+	retval = ERROR_FAIL;
+
+	/* ARMv7-M: A5.3.4 Branches and miscellaneous control */
+	if ((opcode & 0x18008000) == 0x10008000)
+		retval = t2ev_b_misc(opcode, address, instruction, cp);
+
+	/* FIXME decode more 32-bit instructions */
+
+	if (retval == ERROR_OK)
+		return retval;
+
+	if (retval == ERROR_INVALID_ARGUMENTS) {
+		instruction->type = ARM_UNDEFINED_INSTRUCTION;
+		strcpy(cp, "UNDEFINED OPCODE");
+		return ERROR_OK;
+	}
 
 	LOG_DEBUG("Can't decode 32-bit Thumb2 yet (opcode=%08x)", opcode);
 
-	snprintf(instruction->text, 128,
-			"0x%8.8" PRIx32 "  0x%8.8x\t... 32-bit Thumb2 ...",
-			address, opcode);
+	strcpy(cp, "(32-bit Thumb2 ...)");
 	return ERROR_OK;
 }
 
-- 
cgit v1.2.3