diff --git a/src/jtag/interface.c b/src/jtag/interface.c
index fcdb8ee7fc2c2c8c551e8d90c7dc552caa8ef2ec..e475b482d6b0b59aba9d33676d62cb9adf8532bd 100644
--- a/src/jtag/interface.c
+++ b/src/jtag/interface.c
@@ -73,20 +73,23 @@ tap_state_t tap_get_end_state()
 
 int tap_move_ndx(tap_state_t astate)
 {
-	/* given a stable state, return the index into the tms_seqs[] array within tap_get_tms_path() */
+	/* given a stable state, return the index into the tms_seqs[]
+	 * array within tap_get_tms_path()
+	 */
 
 	int ndx;
 
 	switch (astate)
 	{
 	case TAP_RESET:		ndx = 0;			break;
+	case TAP_IDLE:		ndx = 1;			break;
 	case TAP_DRSHIFT:	ndx = 2;			break;
 	case TAP_DRPAUSE:	ndx = 3;			break;
-	case TAP_IDLE:		ndx = 1;			break;
 	case TAP_IRSHIFT:	ndx = 4;			break;
 	case TAP_IRPAUSE:	ndx = 5;			break;
 	default:
-		LOG_ERROR("fatal: unstable state \"%s\" used in tap_move_ndx()", tap_state_name(astate));
+		LOG_ERROR("FATAL: unstable state \"%s\" in tap_move_ndx()",
+				tap_state_name(astate));
 		exit(1);
 	}
 
@@ -95,12 +98,7 @@ int tap_move_ndx(tap_state_t astate)
 
 
 /* tap_move[i][j]: tap movement command to go from state i to state j
- * 0: Test-Logic-Reset
- * 1: Run-Test/Idle
- * 2: Shift-DR
- * 3: Pause-DR
- * 4: Shift-IR
- * 5: Pause-IR
+ * encodings of i and j are what tap_move_ndx() reports.
  *
  * DRSHIFT->DRSHIFT and IRSHIFT->IRSHIFT have to be caught in interface specific code
  */
@@ -108,7 +106,6 @@ struct tms_sequences
 {
 	uint8_t	bits;
 	uint8_t	bit_count;
-
 };
 
 /*
@@ -333,47 +330,54 @@ tap_state_t tap_state_transition(tap_state_t cur_state, bool tms)
 	return new_state;
 }
 
-const char* tap_state_name(tap_state_t state)
+
+/* NOTE:  do not change these state names.  They're documented,
+ * and we rely on them to match SVF input (except for "RUN/IDLE").
+ */
+static const struct name_mapping {
+	enum tap_state	symbol;
+	const char	*name;
+} tap_name_mapping[] = {
+	{ TAP_RESET,	"RESET", },
+	{ TAP_IDLE,	"RUN/IDLE", },
+	{ TAP_DRSELECT,	"DRSELECT", },
+	{ TAP_DRCAPTURE,"DRCAPTURE", },
+	{ TAP_DRSHIFT,	"DRSHIFT", },
+	{ TAP_DREXIT1,	"DREXIT1", },
+	{ TAP_DRPAUSE,	"DRPAUSE", },
+	{ TAP_DREXIT2,	"DREXIT2", },
+	{ TAP_DRUPDATE,	"DRUPDATE", },
+	{ TAP_IRSELECT,	"IRSELECT", },
+	{ TAP_IRCAPTURE,"IRCAPTURE", },
+	{ TAP_IRSHIFT,	"IRSHIFT", },
+	{ TAP_IREXIT1,	"IREXIT1", },
+	{ TAP_IRPAUSE,	"IRPAUSE", },
+	{ TAP_IREXIT2,	"IREXIT2", },
+	{ TAP_IRUPDATE,	"IRUPDATE", },
+
+	/* only for input:  accept standard SVF name */
+	{ TAP_IDLE,	"IDLE", },
+};
+
+const char *tap_state_name(tap_state_t state)
 {
-	const char* ret;
+	unsigned i;
 
-	switch (state)
-	{
-	case TAP_RESET:		ret = "RESET";			break;
-	case TAP_IDLE:		ret = "RUN/IDLE";		break;
-	case TAP_DRSELECT:	ret = "DRSELECT";		break;
-	case TAP_DRCAPTURE: ret = "DRCAPTURE";		break;
-	case TAP_DRSHIFT:	ret = "DRSHIFT";			break;
-	case TAP_DREXIT1:	ret = "DREXIT1";			break;
-	case TAP_DRPAUSE:	ret = "DRPAUSE";			break;
-	case TAP_DREXIT2:	ret = "DREXIT2";			break;
-	case TAP_DRUPDATE:	ret = "DRUPDATE";		break;
-	case TAP_IRSELECT:	ret = "IRSELECT";		break;
-	case TAP_IRCAPTURE: ret = "IRCAPTURE";		break;
-	case TAP_IRSHIFT:	ret = "IRSHIFT";			break;
-	case TAP_IREXIT1:	ret = "IREXIT1";			break;
-	case TAP_IRPAUSE:	ret = "IRPAUSE";			break;
-	case TAP_IREXIT2:	ret = "IREXIT2";			break;
-	case TAP_IRUPDATE:	ret = "IRUPDATE";		break;
-	default:				ret = "???";
+	for (i = 0; i < DIM(tap_name_mapping); i++) {
+		if (tap_name_mapping[i].symbol == state)
+			return tap_name_mapping[i].name;
 	}
-
-	return ret;
+	return "???";
 }
 
 tap_state_t tap_state_by_name(const char *name)
 {
-	tap_state_t x;
-
-	/* standard SVF name is "IDLE" */
-	if (0 == strcasecmp(name, "IDLE"))
-		return TAP_IDLE;
+	unsigned i;
 
-	for (x = 0 ; x < TAP_NUM_STATES ; x++) {
+	for (i = 0; i < DIM(tap_name_mapping); i++) {
 		/* be nice to the human */
-		if (0 == strcasecmp(name, tap_state_name(x))) {
-			return x;
-		}
+		if (strcasecmp(name, tap_name_mapping[i].name) == 0)
+			return tap_name_mapping[i].symbol;
 	}
 	/* not found */
 	return TAP_INVALID;
diff --git a/src/jtag/jtag.h b/src/jtag/jtag.h
index 1dae00fa31607a7a783b88a4c8ca24065444573d..ca09f923e1f1fb79bea514b877e2d8aa90d67489 100644
--- a/src/jtag/jtag.h
+++ b/src/jtag/jtag.h
@@ -57,13 +57,17 @@
  *
  * These definitions were gleaned from the ARM7TDMI-S Technical
  * Reference Manual and validated against several other ARM core
- * technical manuals.  tap_get_tms_path() is sensitive to this numbering
- * and ordering of the TAP states; furthermore, some interfaces require
- * specific numbers be used, as they are handed-off directly to their
- * hardware implementations.
+ * technical manuals.
+ *
+ * FIXME some interfaces require specific numbers be used, as they
+ * are handed-off directly to their hardware implementations.
+ * Fix those drivers to map as appropriate ... then pick some
+ * sane set of numbers here (where 0/uninitialized == INVALID).
  */
 typedef enum tap_state
 {
+	TAP_INVALID = -1,
+
 #if BUILD_ZY1000
 	/* These are the old numbers. Leave as-is for now... */
 	TAP_RESET    = 0, TAP_IDLE = 8,
@@ -72,7 +76,6 @@ typedef enum tap_state
 	TAP_IRSELECT = 9, TAP_IRCAPTURE = 10, TAP_IRSHIFT = 11, TAP_IREXIT1 = 12,
 	TAP_IRPAUSE  = 13, TAP_IREXIT2 = 14, TAP_IRUPDATE = 15,
 
-	TAP_NUM_STATES = 16, TAP_INVALID = -1,
 #else
 	/* Proper ARM recommended numbers */
 	TAP_DREXIT2 = 0x0,
@@ -92,9 +95,6 @@ typedef enum tap_state
 	TAP_IRCAPTURE = 0xe,
 	TAP_RESET = 0x0f,
 
-	TAP_NUM_STATES = 0x10,
-
-	TAP_INVALID = -1,
 #endif
 } tap_state_t;
 
diff --git a/src/jtag/tcl.c b/src/jtag/tcl.c
index e080279a006563173d1fb65c5dd1b7cfee77db65..6a6e3ae018e8c253c761a1ef762f63c78264795b 100644
--- a/src/jtag/tcl.c
+++ b/src/jtag/tcl.c
@@ -1248,6 +1248,8 @@ static int handle_runtest_command(struct command_context_s *cmd_ctx,
  * For "irscan" or "drscan" commands, the "end" (really, "next") state
  * should be stable ... and *NOT* a shift state, otherwise free-running
  * jtag clocks could change the values latched by the update state.
+ * Not surprisingly, this is the same constraint as SVF; the "irscan"
+ * and "drscan" commands are a write-only subset of what SVF provides.
  */
 static bool scan_is_safe(tap_state_t state)
 {
@@ -1285,25 +1287,14 @@ static int handle_irscan_command(struct command_context_s *cmd_ctx, char *cmd, c
 	if (argc >= 4) {
 		/* have at least one pair of numbers. */
 		/* is last pair the magic text? */
-		if (0 == strcmp("-endstate", args[ argc - 2 ])) {
-			const char *cpA;
-			const char *cpS;
-			cpA = args[ argc-1 ];
-			for (endstate = 0 ; endstate < TAP_NUM_STATES ; endstate++) {
-				cpS = tap_state_name(endstate);
-				if (0 == strcmp(cpA, cpS)) {
-					break;
-				}
-			}
-			if (endstate >= TAP_NUM_STATES) {
+		if (strcmp("-endstate", args[argc - 2]) == 0) {
+			endstate = tap_state_by_name(args[argc - 1]);
+			if (endstate == TAP_INVALID)
 				return ERROR_COMMAND_SYNTAX_ERROR;
-			} else {
-				if (!scan_is_safe(endstate))
-					LOG_WARNING("irscan with unsafe "
-							"endstate \"%s\"", cpA);
-				/* found - remove the last 2 args */
-				argc -= 2;
-			}
+			if (!scan_is_safe(endstate))
+				LOG_WARNING("unstable irscan endstate \"%s\"",
+						args[argc - 1]);
+			argc -= 2;
 		}
 	}