1254 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1254 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* $Id$ */
 | 
						||
 | 
						||
#include <common.h>
 | 
						||
 | 
						||
#include <linux/ctype.h>
 | 
						||
#include <bedbug/bedbug.h>
 | 
						||
#include <bedbug/ppc.h>
 | 
						||
#include <bedbug/regs.h>
 | 
						||
#include <bedbug/tables.h>
 | 
						||
 | 
						||
#define Elf32_Word	unsigned long
 | 
						||
 | 
						||
/* USE_SOURCE_CODE enables some symbolic debugging functions of this
 | 
						||
   code.  This is only useful if the program will have access to the
 | 
						||
   source code for the binary being examined.
 | 
						||
*/
 | 
						||
 | 
						||
/* #define USE_SOURCE_CODE 1 */
 | 
						||
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
extern int line_info_from_addr __P ((Elf32_Word, char *, char *, int *));
 | 
						||
extern struct symreflist *symByAddr;
 | 
						||
extern char *symbol_name_from_addr __P ((Elf32_Word, int, int *));
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
 | 
						||
int print_operands __P ((struct ppc_ctx *));
 | 
						||
int get_operand_value __P ((struct opcode *, unsigned long,
 | 
						||
				enum OP_FIELD, unsigned long *));
 | 
						||
struct opcode *find_opcode __P ((unsigned long));
 | 
						||
struct opcode *find_opcode_by_name __P ((char *));
 | 
						||
char *spr_name __P ((int));
 | 
						||
int spr_value __P ((char *));
 | 
						||
char *tbr_name __P ((int));
 | 
						||
int tbr_value __P ((char *));
 | 
						||
int parse_operand __P ((unsigned long, struct opcode *,
 | 
						||
			struct operand *, char *, int *));
 | 
						||
int get_word __P ((char **, char *));
 | 
						||
long read_number __P ((char *));
 | 
						||
int downstring __P ((char *));
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Entry point for the PPC disassembler.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	memaddr		The address to start disassembling from.
 | 
						||
 *
 | 
						||
 *	virtual		If this value is non-zero, then this will be
 | 
						||
 *			used as the base address for the output and
 | 
						||
 *			symbol lookups.  If this value is zero then
 | 
						||
 *			memaddr is used as the absolute address.
 | 
						||
 *
 | 
						||
 *	num_instr	The number of instructions to disassemble.  Since
 | 
						||
 *			each instruction is 32 bits long, this can be
 | 
						||
 *			computed if you know the total size of the region.
 | 
						||
 *
 | 
						||
 *	pfunc		The address of a function that is called to print
 | 
						||
 *			each line of output.  The function should take a
 | 
						||
 *			single character pointer as its parameters a la puts.
 | 
						||
 *
 | 
						||
 *	flags		Sets options for the output.  This is a
 | 
						||
 *			bitwise-inclusive-OR of the following
 | 
						||
 *			values.  Note that only one of the radix
 | 
						||
 *			options may be set.
 | 
						||
 *
 | 
						||
 *			F_RADOCTAL	- output radix is unsigned base 8.
 | 
						||
 *			F_RADUDECIMAL	- output radix is unsigned base 10.
 | 
						||
 *			F_RADSDECIMAL	- output radix is signed base 10.
 | 
						||
 *			F_RADHEX	- output radix is unsigned base 16.
 | 
						||
 *			F_SIMPLE	- use simplified mnemonics.
 | 
						||
 *			F_SYMBOL	- lookup symbols for addresses.
 | 
						||
 *			F_INSTR		- output raw instruction.
 | 
						||
 *			F_LINENO	- show line # info if available.
 | 
						||
 *
 | 
						||
 * Returns true if the area was successfully disassembled or false if
 | 
						||
 * a problem was encountered with accessing the memory.
 | 
						||
 */
 | 
						||
 | 
						||
int disppc (unsigned char *memaddr, unsigned char *virtual, int num_instr,
 | 
						||
			int (*pfunc) (const char *), unsigned long flags)
 | 
						||
{
 | 
						||
	int i;
 | 
						||
	struct ppc_ctx ctx;
 | 
						||
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
	int line_no = 0;
 | 
						||
	int last_line_no = 0;
 | 
						||
	char funcname[128] = { 0 };
 | 
						||
	char filename[256] = { 0 };
 | 
						||
	char last_funcname[128] = { 0 };
 | 
						||
	int symoffset;
 | 
						||
	char *symname;
 | 
						||
	char *cursym = (char *) 0;
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	ctx.flags = flags;
 | 
						||
	ctx.virtual = virtual;
 | 
						||
 | 
						||
	/* Figure out the output radix before we go any further */
 | 
						||
 | 
						||
	if (ctx.flags & F_RADOCTAL) {
 | 
						||
		/* Unsigned octal output */
 | 
						||
		strcpy (ctx.radix_fmt, "O%o");
 | 
						||
	} else if (ctx.flags & F_RADUDECIMAL) {
 | 
						||
		/* Unsigned decimal output */
 | 
						||
		strcpy (ctx.radix_fmt, "%u");
 | 
						||
	} else if (ctx.flags & F_RADSDECIMAL) {
 | 
						||
		/* Signed decimal output */
 | 
						||
		strcpy (ctx.radix_fmt, "%d");
 | 
						||
	} else {
 | 
						||
		/* Unsigned hex output */
 | 
						||
		strcpy (ctx.radix_fmt, "0x%x");
 | 
						||
	}
 | 
						||
 | 
						||
	if (ctx.virtual == 0) {
 | 
						||
		ctx.virtual = memaddr;
 | 
						||
	}
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
	if (ctx.flags & F_SYMBOL) {
 | 
						||
		if (symByAddr == 0)		/* no symbols loaded */
 | 
						||
			ctx.flags &= ~F_SYMBOL;
 | 
						||
		else {
 | 
						||
			cursym = (char *) 0;
 | 
						||
			symoffset = 0;
 | 
						||
		}
 | 
						||
	}
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
 | 
						||
	/* format each line as "XXXXXXXX: <symbol> IIIIIIII  disassembly" where,
 | 
						||
	   XXXXXXXX is the memory address in hex,
 | 
						||
	   <symbol> is the symbolic location if F_SYMBOL is set.
 | 
						||
	   IIIIIIII is the raw machine code in hex if F_INSTR is set,
 | 
						||
	   and disassembly is the disassembled machine code with numbers
 | 
						||
	   formatted according to the 'radix' parameter */
 | 
						||
 | 
						||
	for (i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4) {
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
		if (ctx.flags & F_LINENO) {
 | 
						||
			if ((line_info_from_addr ((Elf32_Word) ctx.virtual,
 | 
						||
				filename, funcname, &line_no) == true) &&
 | 
						||
				((line_no != last_line_no) ||
 | 
						||
				 (strcmp (last_funcname, funcname) != 0))) {
 | 
						||
				print_source_line (filename, funcname, line_no, pfunc);
 | 
						||
			}
 | 
						||
			last_line_no = line_no;
 | 
						||
			strcpy (last_funcname, funcname);
 | 
						||
		}
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
 | 
						||
		sprintf (ctx.data, "%08lx: ", (unsigned long) ctx.virtual);
 | 
						||
		ctx.datalen = 10;
 | 
						||
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
		if (ctx.flags & F_SYMBOL) {
 | 
						||
			if ((symname =
 | 
						||
				 symbol_name_from_addr((Elf32_Word) ctx.virtual,
 | 
						||
						true, 0)) != 0) {
 | 
						||
				cursym = symname;
 | 
						||
				symoffset = 0;
 | 
						||
			} else {
 | 
						||
				if ((cursym == 0) &&
 | 
						||
					((symname =
 | 
						||
					  symbol_name_from_addr((Elf32_Word) ctx.virtual,
 | 
						||
						false, &symoffset)) != 0)) {
 | 
						||
					cursym = symname;
 | 
						||
				} else {
 | 
						||
					symoffset += 4;
 | 
						||
				}
 | 
						||
			}
 | 
						||
 | 
						||
			if (cursym != 0) {
 | 
						||
				sprintf (&ctx.data[ctx.datalen], "<%s+", cursym);
 | 
						||
				ctx.datalen = strlen (ctx.data);
 | 
						||
				sprintf (&ctx.data[ctx.datalen], ctx.radix_fmt, symoffset);
 | 
						||
				strcat (ctx.data, ">");
 | 
						||
				ctx.datalen = strlen (ctx.data);
 | 
						||
			}
 | 
						||
		}
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
 | 
						||
		ctx.instr = INSTRUCTION (memaddr);
 | 
						||
 | 
						||
		if (ctx.flags & F_INSTR) {
 | 
						||
			/* Find the opcode structure for this opcode.  If one is not found
 | 
						||
			   then it must be an illegal instruction */
 | 
						||
			sprintf (&ctx.data[ctx.datalen],
 | 
						||
					 "   %02lx %02lx %02lx %02lx    ",
 | 
						||
					 ((ctx.instr >> 24) & 0xff),
 | 
						||
					 ((ctx.instr >> 16) & 0xff), ((ctx.instr >> 8) & 0xff),
 | 
						||
					 (ctx.instr & 0xff));
 | 
						||
			ctx.datalen += 18;
 | 
						||
		} else {
 | 
						||
			strcat (ctx.data, "   ");
 | 
						||
			ctx.datalen += 3;
 | 
						||
		}
 | 
						||
 | 
						||
		if ((ctx.op = find_opcode (ctx.instr)) == 0) {
 | 
						||
			/* Illegal Opcode */
 | 
						||
			sprintf (&ctx.data[ctx.datalen], "        .long 0x%08lx",
 | 
						||
					 ctx.instr);
 | 
						||
			ctx.datalen += 24;
 | 
						||
			(*pfunc) (ctx.data);
 | 
						||
			continue;
 | 
						||
		}
 | 
						||
 | 
						||
		if (((ctx.flags & F_SIMPLE) == 0) ||
 | 
						||
			(ctx.op->hfunc == 0) ||
 | 
						||
			((*ctx.op->hfunc) (&ctx) == false)) {
 | 
						||
			sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name);
 | 
						||
			ctx.datalen += 8;
 | 
						||
			print_operands (&ctx);
 | 
						||
		}
 | 
						||
 | 
						||
		(*pfunc) (ctx.data);
 | 
						||
	}
 | 
						||
 | 
						||
	return true;
 | 
						||
}								/* disppc */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Called by the disassembler to print the operands for an instruction.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	ctx		A pointer to the disassembler context record.
 | 
						||
 *
 | 
						||
 * always returns 0.
 | 
						||
 */
 | 
						||
 | 
						||
int print_operands (struct ppc_ctx *ctx)
 | 
						||
{
 | 
						||
	int open_parens = 0;
 | 
						||
	int field;
 | 
						||
	unsigned long operand;
 | 
						||
	struct operand *opr;
 | 
						||
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
	char *symname;
 | 
						||
	int offset;
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	/* Walk through the operands and list each in order */
 | 
						||
	for (field = 0; ctx->op->fields[field] != 0; ++field) {
 | 
						||
		if (ctx->op->fields[field] > n_operands) {
 | 
						||
			continue;			/* bad operand ?! */
 | 
						||
		}
 | 
						||
 | 
						||
		opr = &operands[ctx->op->fields[field] - 1];
 | 
						||
 | 
						||
		if (opr->hint & OH_SILENT) {
 | 
						||
			continue;
 | 
						||
		}
 | 
						||
 | 
						||
		if ((field > 0) && !open_parens) {
 | 
						||
			strcat (ctx->data, ",");
 | 
						||
			ctx->datalen++;
 | 
						||
		}
 | 
						||
 | 
						||
		operand = (ctx->instr >> opr->shift) & ((1 << opr->bits) - 1);
 | 
						||
 | 
						||
		if (opr->hint & OH_ADDR) {
 | 
						||
			if ((operand & (1 << (opr->bits - 1))) != 0) {
 | 
						||
				operand = operand - (1 << opr->bits);
 | 
						||
			}
 | 
						||
 | 
						||
			if (ctx->op->hint & H_RELATIVE)
 | 
						||
				operand = (operand << 2) + (unsigned long) ctx->virtual;
 | 
						||
			else
 | 
						||
				operand = (operand << 2);
 | 
						||
 | 
						||
 | 
						||
			sprintf (&ctx->data[ctx->datalen], "0x%lx", operand);
 | 
						||
			ctx->datalen = strlen (ctx->data);
 | 
						||
 | 
						||
#ifdef USE_SOURCE_CODE
 | 
						||
			if ((ctx->flags & F_SYMBOL) &&
 | 
						||
				((symname =
 | 
						||
				  symbol_name_from_addr (operand, 0, &offset)) != 0)) {
 | 
						||
				sprintf (&ctx->data[ctx->datalen], " <%s", symname);
 | 
						||
				if (offset != 0) {
 | 
						||
					strcat (ctx->data, "+");
 | 
						||
					ctx->datalen = strlen (ctx->data);
 | 
						||
					sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt,
 | 
						||
							 offset);
 | 
						||
				}
 | 
						||
				strcat (ctx->data, ">");
 | 
						||
			}
 | 
						||
#endif /* USE_SOURCE_CODE */
 | 
						||
		}
 | 
						||
 | 
						||
		else if (opr->hint & OH_REG) {
 | 
						||
			if ((operand == 0) &&
 | 
						||
				(opr->field == O_rA) && (ctx->op->hint & H_RA0_IS_0)) {
 | 
						||
				strcat (ctx->data, "0");
 | 
						||
			} else {
 | 
						||
				sprintf (&ctx->data[ctx->datalen], "r%d", (short) operand);
 | 
						||
			}
 | 
						||
 | 
						||
			if (open_parens) {
 | 
						||
				strcat (ctx->data, ")");
 | 
						||
				open_parens--;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		else if (opr->hint & OH_SPR) {
 | 
						||
			strcat (ctx->data, spr_name (operand));
 | 
						||
		}
 | 
						||
 | 
						||
		else if (opr->hint & OH_TBR) {
 | 
						||
			strcat (ctx->data, tbr_name (operand));
 | 
						||
		}
 | 
						||
 | 
						||
		else if (opr->hint & OH_LITERAL) {
 | 
						||
			switch (opr->field) {
 | 
						||
			case O_cr2:
 | 
						||
				strcat (ctx->data, "cr2");
 | 
						||
				ctx->datalen += 3;
 | 
						||
				break;
 | 
						||
 | 
						||
			default:
 | 
						||
				break;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		else {
 | 
						||
			sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt,
 | 
						||
					 (unsigned short) operand);
 | 
						||
 | 
						||
			if (open_parens) {
 | 
						||
				strcat (ctx->data, ")");
 | 
						||
				open_parens--;
 | 
						||
			}
 | 
						||
 | 
						||
			else if (opr->hint & OH_OFFSET) {
 | 
						||
				strcat (ctx->data, "(");
 | 
						||
				open_parens++;
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		ctx->datalen = strlen (ctx->data);
 | 
						||
	}
 | 
						||
 | 
						||
	return 0;
 | 
						||
}								/* print_operands */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Called to get the value of an arbitrary operand with in an instruction.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	op		The pointer to the opcode structure to which
 | 
						||
 *			the operands belong.
 | 
						||
 *
 | 
						||
 *	instr		The instruction (32 bits) containing the opcode
 | 
						||
 *			and the operands to print.  By the time that
 | 
						||
 *			this routine is called the operand has already
 | 
						||
 *			been added to the output.
 | 
						||
 *
 | 
						||
 *	field		The field (operand) to get the value of.
 | 
						||
 *
 | 
						||
 *	value		The address of an unsigned long to be filled in
 | 
						||
 *			with the value of the operand if it is found.  This
 | 
						||
 *			will only be filled in if the function returns
 | 
						||
 *			true.  This may be passed as 0 if the value is
 | 
						||
 *			not required.
 | 
						||
 *
 | 
						||
 * Returns true if the operand was found or false if it was not.
 | 
						||
 */
 | 
						||
 | 
						||
int get_operand_value (struct opcode *op, unsigned long instr,
 | 
						||
					   enum OP_FIELD field, unsigned long *value)
 | 
						||
{
 | 
						||
	int i;
 | 
						||
	struct operand *opr;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (field > n_operands) {
 | 
						||
		return false;			/* bad operand ?! */
 | 
						||
	}
 | 
						||
 | 
						||
	/* Walk through the operands and list each in order */
 | 
						||
	for (i = 0; op->fields[i] != 0; ++i) {
 | 
						||
		if (op->fields[i] != field) {
 | 
						||
			continue;
 | 
						||
		}
 | 
						||
 | 
						||
		opr = &operands[op->fields[i] - 1];
 | 
						||
 | 
						||
		if (value) {
 | 
						||
			*value = (instr >> opr->shift) & ((1 << opr->bits) - 1);
 | 
						||
		}
 | 
						||
		return true;
 | 
						||
	}
 | 
						||
 | 
						||
	return false;
 | 
						||
}								/* operand_value */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Called by the disassembler to match an opcode value to an opcode structure.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	instr		The instruction (32 bits) to match.  This value
 | 
						||
 *			may contain operand values as well as the opcode
 | 
						||
 *			since they will be masked out anyway for this
 | 
						||
 *			search.
 | 
						||
 *
 | 
						||
 * Returns the address of an opcode struct (from the opcode table) if the
 | 
						||
 * operand successfully matched an entry, or 0 if no match was found.
 | 
						||
 */
 | 
						||
 | 
						||
struct opcode *find_opcode (unsigned long instr)
 | 
						||
{
 | 
						||
	struct opcode *ptr;
 | 
						||
	int top = 0;
 | 
						||
	int bottom = n_opcodes - 1;
 | 
						||
	int idx;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	while (top <= bottom) {
 | 
						||
		idx = (top + bottom) >> 1;
 | 
						||
		ptr = &opcodes[idx];
 | 
						||
 | 
						||
		if ((instr & ptr->mask) < ptr->opcode) {
 | 
						||
			bottom = idx - 1;
 | 
						||
		} else if ((instr & ptr->mask) > ptr->opcode) {
 | 
						||
			top = idx + 1;
 | 
						||
		} else {
 | 
						||
			return ptr;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return (struct opcode *) 0;
 | 
						||
}								/* find_opcode */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Called by the assembler to match an opcode name to an opcode structure.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	name		The text name of the opcode, e.g. "b", "mtspr", etc.
 | 
						||
 *
 | 
						||
 * The opcodes are sorted numerically by their instruction binary code
 | 
						||
 * so a search for the name cannot use the binary search used by the
 | 
						||
 * other find routine.
 | 
						||
 *
 | 
						||
 * Returns the address of an opcode struct (from the opcode table) if the
 | 
						||
 * name successfully matched an entry, or 0 if no match was found.
 | 
						||
 */
 | 
						||
 | 
						||
struct opcode *find_opcode_by_name (char *name)
 | 
						||
{
 | 
						||
	int idx;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	downstring (name);
 | 
						||
 | 
						||
	for (idx = 0; idx < n_opcodes; ++idx) {
 | 
						||
		if (!strcmp (name, opcodes[idx].name))
 | 
						||
			return &opcodes[idx];
 | 
						||
	}
 | 
						||
 | 
						||
	return (struct opcode *) 0;
 | 
						||
}								/* find_opcode_by_name */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Convert the 'spr' operand from its numeric value to its symbolic name.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	value		The value of the 'spr' operand.  This value should
 | 
						||
 *			be unmodified from its encoding in the instruction.
 | 
						||
 *			the split-field computations will be performed
 | 
						||
 *			here before the switch.
 | 
						||
 *
 | 
						||
 * Returns the address of a character array containing the name of the
 | 
						||
 * special purpose register defined by the 'value' parameter, or the
 | 
						||
 * address of a character array containing "???" if no match was found.
 | 
						||
 */
 | 
						||
 | 
						||
char *spr_name (int value)
 | 
						||
{
 | 
						||
	unsigned short spr;
 | 
						||
	static char other[10];
 | 
						||
	int i;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	/* spr is a 10 bit field whose interpretation has the high and low
 | 
						||
	   five-bit fields reversed from their encoding in the operand */
 | 
						||
 | 
						||
	spr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5);
 | 
						||
 | 
						||
	for (i = 0; i < n_sprs; ++i) {
 | 
						||
		if (spr == spr_map[i].spr_val)
 | 
						||
			return spr_map[i].spr_name;
 | 
						||
	}
 | 
						||
 | 
						||
	sprintf (other, "%d", spr);
 | 
						||
	return other;
 | 
						||
}								/* spr_name */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Convert the 'spr' operand from its symbolic name to its numeric value
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	name		The symbolic name of the 'spr' operand.  The
 | 
						||
 *			split-field encoding will be done by this routine.
 | 
						||
 *			NOTE: name can be a number.
 | 
						||
 *
 | 
						||
 * Returns the numeric value for the spr appropriate for encoding a machine
 | 
						||
 * instruction.  Returns 0 if unable to find the SPR.
 | 
						||
 */
 | 
						||
 | 
						||
int spr_value (char *name)
 | 
						||
{
 | 
						||
	struct spr_info *sprp;
 | 
						||
	int spr;
 | 
						||
	int i;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (!name || !*name)
 | 
						||
		return 0;
 | 
						||
 | 
						||
	if (isdigit ((int) name[0])) {
 | 
						||
		i = htonl (read_number (name));
 | 
						||
		spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5);
 | 
						||
		return spr;
 | 
						||
	}
 | 
						||
 | 
						||
	downstring (name);
 | 
						||
 | 
						||
	for (i = 0; i < n_sprs; ++i) {
 | 
						||
		sprp = &spr_map[i];
 | 
						||
 | 
						||
		if (strcmp (name, sprp->spr_name) == 0) {
 | 
						||
			/* spr is a 10 bit field whose interpretation has the high and low
 | 
						||
			   five-bit fields reversed from their encoding in the operand */
 | 
						||
			i = htonl (sprp->spr_val);
 | 
						||
			spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5);
 | 
						||
 | 
						||
			return spr;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return 0;
 | 
						||
}								/* spr_value */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Convert the 'tbr' operand from its numeric value to its symbolic name.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	value		The value of the 'tbr' operand.  This value should
 | 
						||
 *			be unmodified from its encoding in the instruction.
 | 
						||
 *			the split-field computations will be performed
 | 
						||
 *			here before the switch.
 | 
						||
 *
 | 
						||
 * Returns the address of a character array containing the name of the
 | 
						||
 * time base register defined by the 'value' parameter, or the address
 | 
						||
 * of a character array containing "???" if no match was found.
 | 
						||
 */
 | 
						||
 | 
						||
char *tbr_name (int value)
 | 
						||
{
 | 
						||
	unsigned short tbr;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	/* tbr is a 10 bit field whose interpretation has the high and low
 | 
						||
	   five-bit fields reversed from their encoding in the operand */
 | 
						||
 | 
						||
	tbr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5);
 | 
						||
 | 
						||
	if (tbr == 268)
 | 
						||
		return "TBL";
 | 
						||
 | 
						||
	else if (tbr == 269)
 | 
						||
		return "TBU";
 | 
						||
 | 
						||
 | 
						||
	return "???";
 | 
						||
}								/* tbr_name */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Convert the 'tbr' operand from its symbolic name to its numeric value.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	name		The symbolic name of the 'tbr' operand.  The
 | 
						||
 *			split-field encoding will be done by this routine.
 | 
						||
 *
 | 
						||
 * Returns the numeric value for the spr appropriate for encoding a machine
 | 
						||
 * instruction.  Returns 0 if unable to find the TBR.
 | 
						||
 */
 | 
						||
 | 
						||
int tbr_value (char *name)
 | 
						||
{
 | 
						||
	int tbr;
 | 
						||
	int val;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (!name || !*name)
 | 
						||
		return 0;
 | 
						||
 | 
						||
	downstring (name);
 | 
						||
 | 
						||
	if (isdigit ((int) name[0])) {
 | 
						||
		val = read_number (name);
 | 
						||
 | 
						||
		if (val != 268 && val != 269)
 | 
						||
			return 0;
 | 
						||
	} else if (strcmp (name, "tbl") == 0)
 | 
						||
		val = 268;
 | 
						||
	else if (strcmp (name, "tbu") == 0)
 | 
						||
		val = 269;
 | 
						||
	else
 | 
						||
		return 0;
 | 
						||
 | 
						||
	/* tbr is a 10 bit field whose interpretation has the high and low
 | 
						||
	   five-bit fields reversed from their encoding in the operand */
 | 
						||
 | 
						||
	val = htonl (val);
 | 
						||
	tbr = ((val >> 5) & 0x1f) | ((val & 0x1f) << 5);
 | 
						||
	return tbr;
 | 
						||
}								/* tbr_name */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * The next several functions (handle_xxx) are the routines that handle
 | 
						||
 * disassembling the opcodes with simplified mnemonics.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	ctx		A pointer to the disassembler context record.
 | 
						||
 *
 | 
						||
 * Returns true if the simpler form was printed or false if it was not.
 | 
						||
 */
 | 
						||
 | 
						||
int handle_bc (struct ppc_ctx *ctx)
 | 
						||
{
 | 
						||
	unsigned long bo;
 | 
						||
	unsigned long bi;
 | 
						||
	static struct opcode blt = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0},
 | 
						||
	0, "blt", H_RELATIVE
 | 
						||
	};
 | 
						||
	static struct opcode bne =
 | 
						||
			{ B_OPCODE (16, 0, 0), B_MASK, {O_cr2, O_BD, 0},
 | 
						||
	0, "bne", H_RELATIVE
 | 
						||
	};
 | 
						||
	static struct opcode bdnz = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0},
 | 
						||
	0, "bdnz", H_RELATIVE
 | 
						||
	};
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false)
 | 
						||
		return false;
 | 
						||
 | 
						||
	if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false)
 | 
						||
		return false;
 | 
						||
 | 
						||
	if ((bo == 12) && (bi == 0)) {
 | 
						||
		ctx->op = &blt;
 | 
						||
		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
 | 
						||
		ctx->datalen += 8;
 | 
						||
		print_operands (ctx);
 | 
						||
		return true;
 | 
						||
	} else if ((bo == 4) && (bi == 10)) {
 | 
						||
		ctx->op = =⃥
 | 
						||
		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
 | 
						||
		ctx->datalen += 8;
 | 
						||
		print_operands (ctx);
 | 
						||
		return true;
 | 
						||
	} else if ((bo == 16) && (bi == 0)) {
 | 
						||
		ctx->op = &bdnz;
 | 
						||
		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
 | 
						||
		ctx->datalen += 8;
 | 
						||
		print_operands (ctx);
 | 
						||
		return true;
 | 
						||
	}
 | 
						||
 | 
						||
	return false;
 | 
						||
}								/* handle_blt */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Outputs source line information for the disassembler.  This should
 | 
						||
 * be modified in the future to lookup the actual line of source code
 | 
						||
 * from the file, but for now this will do.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	filename	The address of a character array containing the
 | 
						||
 *			absolute path and file name of the source file.
 | 
						||
 *
 | 
						||
 *	funcname	The address of a character array containing the
 | 
						||
 *			name of the function (not C++ demangled (yet))
 | 
						||
 *			to which this code belongs.
 | 
						||
 *
 | 
						||
 *	line_no		An integer specifying the source line number that
 | 
						||
 *			generated this code.
 | 
						||
 *
 | 
						||
 *	pfunc		The address of a function to call to print the output.
 | 
						||
 *
 | 
						||
 *
 | 
						||
 * Returns true if it was able to output the line info, or false if it was
 | 
						||
 * not.
 | 
						||
 */
 | 
						||
 | 
						||
int print_source_line (char *filename, char *funcname,
 | 
						||
					   int line_no, int (*pfunc) (const char *))
 | 
						||
{
 | 
						||
	char out_buf[256];
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	(*pfunc) ("");				/* output a newline */
 | 
						||
	sprintf (out_buf, "%s %s(): line %d", filename, funcname, line_no);
 | 
						||
	(*pfunc) (out_buf);
 | 
						||
 | 
						||
	return true;
 | 
						||
}								/* print_source_line */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Entry point for the PPC assembler.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	asm_buf		An array of characters containing the assembly opcode
 | 
						||
 *			and operands to convert to a POWERPC machine
 | 
						||
 *			instruction.
 | 
						||
 *
 | 
						||
 * Returns the machine instruction or zero.
 | 
						||
 */
 | 
						||
 | 
						||
unsigned long asmppc (unsigned long memaddr, char *asm_buf, int *err)
 | 
						||
{
 | 
						||
	struct opcode *opc;
 | 
						||
	struct operand *oper[MAX_OPERANDS];
 | 
						||
	unsigned long instr;
 | 
						||
	unsigned long param;
 | 
						||
	char *ptr = asm_buf;
 | 
						||
	char scratch[20];
 | 
						||
	int i;
 | 
						||
	int w_operands = 0;			/* wanted # of operands */
 | 
						||
	int n_operands = 0;			/* # of operands read */
 | 
						||
	int asm_debug = 0;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (err)
 | 
						||
		*err = 0;
 | 
						||
 | 
						||
	if (get_word (&ptr, scratch) == 0)
 | 
						||
		return 0;
 | 
						||
 | 
						||
	/* Lookup the opcode structure based on the opcode name */
 | 
						||
	if ((opc = find_opcode_by_name (scratch)) == (struct opcode *) 0) {
 | 
						||
		if (err)
 | 
						||
			*err = E_ASM_BAD_OPCODE;
 | 
						||
		return 0;
 | 
						||
	}
 | 
						||
 | 
						||
	if (asm_debug) {
 | 
						||
		printf ("asmppc: Opcode = \"%s\"\n", opc->name);
 | 
						||
	}
 | 
						||
 | 
						||
	for (i = 0; i < 8; ++i) {
 | 
						||
		if (opc->fields[i] == 0)
 | 
						||
			break;
 | 
						||
		++w_operands;
 | 
						||
	}
 | 
						||
 | 
						||
	if (asm_debug) {
 | 
						||
		printf ("asmppc: Expecting %d operands\n", w_operands);
 | 
						||
	}
 | 
						||
 | 
						||
	instr = opc->opcode;
 | 
						||
 | 
						||
	/* read each operand */
 | 
						||
	while (n_operands < w_operands) {
 | 
						||
 | 
						||
		oper[n_operands] = &operands[opc->fields[n_operands] - 1];
 | 
						||
 | 
						||
		if (oper[n_operands]->hint & OH_SILENT) {
 | 
						||
			/* Skip silent operands, they are covered in opc->opcode */
 | 
						||
 | 
						||
			if (asm_debug) {
 | 
						||
				printf ("asmppc: Operand %d \"%s\" SILENT\n", n_operands,
 | 
						||
						oper[n_operands]->name);
 | 
						||
			}
 | 
						||
 | 
						||
			++n_operands;
 | 
						||
			continue;
 | 
						||
		}
 | 
						||
 | 
						||
		if (get_word (&ptr, scratch) == 0)
 | 
						||
			break;
 | 
						||
 | 
						||
		if (asm_debug) {
 | 
						||
			printf ("asmppc: Operand %d \"%s\" : \"%s\"\n", n_operands,
 | 
						||
					oper[n_operands]->name, scratch);
 | 
						||
		}
 | 
						||
 | 
						||
		if ((param = parse_operand (memaddr, opc, oper[n_operands],
 | 
						||
									scratch, err)) == -1)
 | 
						||
			return 0;
 | 
						||
 | 
						||
		instr |= param;
 | 
						||
		++n_operands;
 | 
						||
	}
 | 
						||
 | 
						||
	if (n_operands < w_operands) {
 | 
						||
		if (err)
 | 
						||
			*err = E_ASM_NUM_OPERANDS;
 | 
						||
		return 0;
 | 
						||
	}
 | 
						||
 | 
						||
	if (asm_debug) {
 | 
						||
		printf ("asmppc: Instruction = 0x%08lx\n", instr);
 | 
						||
	}
 | 
						||
 | 
						||
	return instr;
 | 
						||
}								/* asmppc */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Called by the assembler to interpret a single operand
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	ctx		A pointer to the disassembler context record.
 | 
						||
 *
 | 
						||
 * Returns 0 if the operand is ok, or -1 if it is bad.
 | 
						||
 */
 | 
						||
 | 
						||
int parse_operand (unsigned long memaddr, struct opcode *opc,
 | 
						||
				   struct operand *oper, char *txt, int *err)
 | 
						||
{
 | 
						||
	long data;
 | 
						||
	long mask;
 | 
						||
	int is_neg = 0;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	mask = (1 << oper->bits) - 1;
 | 
						||
 | 
						||
	if (oper->hint & OH_ADDR) {
 | 
						||
		data = read_number (txt);
 | 
						||
 | 
						||
		if (opc->hint & H_RELATIVE)
 | 
						||
			data = data - memaddr;
 | 
						||
 | 
						||
		if (data < 0)
 | 
						||
			is_neg = 1;
 | 
						||
 | 
						||
		data >>= 2;
 | 
						||
		data &= (mask >> 1);
 | 
						||
 | 
						||
		if (is_neg)
 | 
						||
			data |= 1 << (oper->bits - 1);
 | 
						||
	}
 | 
						||
 | 
						||
	else if (oper->hint & OH_REG) {
 | 
						||
		if (txt[0] == 'r' || txt[0] == 'R')
 | 
						||
			txt++;
 | 
						||
		else if (txt[0] == '%' && (txt[1] == 'r' || txt[1] == 'R'))
 | 
						||
			txt += 2;
 | 
						||
 | 
						||
		data = read_number (txt);
 | 
						||
		if (data > 31) {
 | 
						||
			if (err)
 | 
						||
				*err = E_ASM_BAD_REGISTER;
 | 
						||
			return -1;
 | 
						||
		}
 | 
						||
 | 
						||
		data = htonl (data);
 | 
						||
	}
 | 
						||
 | 
						||
	else if (oper->hint & OH_SPR) {
 | 
						||
		if ((data = spr_value (txt)) == 0) {
 | 
						||
			if (err)
 | 
						||
				*err = E_ASM_BAD_SPR;
 | 
						||
			return -1;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	else if (oper->hint & OH_TBR) {
 | 
						||
		if ((data = tbr_value (txt)) == 0) {
 | 
						||
			if (err)
 | 
						||
				*err = E_ASM_BAD_TBR;
 | 
						||
			return -1;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	else {
 | 
						||
		data = htonl (read_number (txt));
 | 
						||
	}
 | 
						||
 | 
						||
	return (data & mask) << oper->shift;
 | 
						||
}								/* parse_operand */
 | 
						||
 | 
						||
 | 
						||
char *asm_error_str (int err)
 | 
						||
{
 | 
						||
	switch (err) {
 | 
						||
	case E_ASM_BAD_OPCODE:
 | 
						||
		return "Bad opcode";
 | 
						||
	case E_ASM_NUM_OPERANDS:
 | 
						||
		return "Bad number of operands";
 | 
						||
	case E_ASM_BAD_REGISTER:
 | 
						||
		return "Bad register number";
 | 
						||
	case E_ASM_BAD_SPR:
 | 
						||
		return "Bad SPR name or number";
 | 
						||
	case E_ASM_BAD_TBR:
 | 
						||
		return "Bad TBR name or number";
 | 
						||
	}
 | 
						||
 | 
						||
	return "";
 | 
						||
}								/* asm_error_str */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Copy a word from one buffer to another, ignores leading white spaces.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	src		The address of a character pointer to the
 | 
						||
 *			source buffer.
 | 
						||
 *	dest		A pointer to a character buffer to write the word
 | 
						||
 *			into.
 | 
						||
 *
 | 
						||
 * Returns the number of non-white space characters copied, or zero.
 | 
						||
 */
 | 
						||
 | 
						||
int get_word (char **src, char *dest)
 | 
						||
{
 | 
						||
	char *ptr = *src;
 | 
						||
	int nchars = 0;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	/* Eat white spaces */
 | 
						||
	while (*ptr && isblank (*ptr))
 | 
						||
		ptr++;
 | 
						||
 | 
						||
	if (*ptr == 0) {
 | 
						||
		*src = ptr;
 | 
						||
		return 0;
 | 
						||
	}
 | 
						||
 | 
						||
	/* Find the text of the word */
 | 
						||
	while (*ptr && !isblank (*ptr) && (*ptr != ','))
 | 
						||
		dest[nchars++] = *ptr++;
 | 
						||
	ptr = (*ptr == ',') ? ptr + 1 : ptr;
 | 
						||
	dest[nchars] = 0;
 | 
						||
 | 
						||
	*src = ptr;
 | 
						||
	return nchars;
 | 
						||
}								/* get_word */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Convert a numeric string to a number, be aware of base notations.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	txt		The numeric string.
 | 
						||
 *
 | 
						||
 * Returns the converted numeric value.
 | 
						||
 */
 | 
						||
 | 
						||
long read_number (char *txt)
 | 
						||
{
 | 
						||
	long val;
 | 
						||
	int is_neg = 0;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (txt == 0 || *txt == 0)
 | 
						||
		return 0;
 | 
						||
 | 
						||
	if (*txt == '-') {
 | 
						||
		is_neg = 1;
 | 
						||
		++txt;
 | 
						||
	}
 | 
						||
 | 
						||
	if (txt[0] == '0' && (txt[1] == 'x' || txt[1] == 'X'))	/* hex */
 | 
						||
		val = simple_strtoul (&txt[2], NULL, 16);
 | 
						||
	else						/* decimal */
 | 
						||
		val = simple_strtoul (txt, NULL, 10);
 | 
						||
 | 
						||
	if (is_neg)
 | 
						||
		val = -val;
 | 
						||
 | 
						||
	return val;
 | 
						||
}								/* read_number */
 | 
						||
 | 
						||
 | 
						||
int downstring (char *s)
 | 
						||
{
 | 
						||
	if (!s || !*s)
 | 
						||
		return 0;
 | 
						||
 | 
						||
	while (*s) {
 | 
						||
		if (isupper (*s))
 | 
						||
			*s = tolower (*s);
 | 
						||
		s++;
 | 
						||
	}
 | 
						||
 | 
						||
	return 0;
 | 
						||
}								/* downstring */
 | 
						||
 | 
						||
 | 
						||
 | 
						||
/*======================================================================
 | 
						||
 * Examines the instruction at the current address and determines the
 | 
						||
 * next address to be executed.  This will take into account branches
 | 
						||
 * of different types so that a "step" and "next" operations can be
 | 
						||
 * supported.
 | 
						||
 *
 | 
						||
 * Arguments:
 | 
						||
 *	nextaddr	The address (to be filled in) of the next
 | 
						||
 *			instruction to execute.  This will only be a valid
 | 
						||
 *			address if true is returned.
 | 
						||
 *
 | 
						||
 *	step_over	A flag indicating how to compute addresses for
 | 
						||
 *			branch statements:
 | 
						||
 *			 true  = Step over the branch (next)
 | 
						||
 *			 false = step into the branch (step)
 | 
						||
 *
 | 
						||
 * Returns true if it was able to compute the address.  Returns false if
 | 
						||
 * it has a problem reading the current instruction or one of the registers.
 | 
						||
 */
 | 
						||
 | 
						||
int find_next_address (unsigned char *nextaddr, int step_over,
 | 
						||
					   struct pt_regs *regs)
 | 
						||
{
 | 
						||
	unsigned long pc;			/* SRR0 register from PPC */
 | 
						||
	unsigned long ctr;			/* CTR register from PPC */
 | 
						||
	unsigned long cr;			/* CR register from PPC */
 | 
						||
	unsigned long lr;			/* LR register from PPC */
 | 
						||
	unsigned long instr;		/* instruction at SRR0 */
 | 
						||
	unsigned long next;			/* computed instruction for 'next' */
 | 
						||
	unsigned long step;			/* computed instruction for 'step' */
 | 
						||
	unsigned long addr = 0;		/* target address operand */
 | 
						||
	unsigned long aa = 0;		/* AA operand */
 | 
						||
	unsigned long lk = 0;		/* LK operand */
 | 
						||
	unsigned long bo = 0;		/* BO operand */
 | 
						||
	unsigned long bi = 0;		/* BI operand */
 | 
						||
	struct opcode *op = 0;		/* opcode structure for 'instr' */
 | 
						||
	int ctr_ok = 0;
 | 
						||
	int cond_ok = 0;
 | 
						||
	int conditional = 0;
 | 
						||
	int branch = 0;
 | 
						||
 | 
						||
  /*------------------------------------------------------------*/
 | 
						||
 | 
						||
	if (nextaddr == 0 || regs == 0) {
 | 
						||
		printf ("find_next_address: bad args");
 | 
						||
		return false;
 | 
						||
	}
 | 
						||
 | 
						||
	pc = regs->nip & 0xfffffffc;
 | 
						||
	instr = INSTRUCTION (pc);
 | 
						||
 | 
						||
	if ((op = find_opcode (instr)) == (struct opcode *) 0) {
 | 
						||
		printf ("find_next_address: can't parse opcode 0x%lx", instr);
 | 
						||
		return false;
 | 
						||
	}
 | 
						||
 | 
						||
	ctr = regs->ctr;
 | 
						||
	cr = regs->ccr;
 | 
						||
	lr = regs->link;
 | 
						||
 | 
						||
	switch (op->opcode) {
 | 
						||
	case B_OPCODE (16, 0, 0):	/* bc */
 | 
						||
	case B_OPCODE (16, 0, 1):	/* bcl */
 | 
						||
	case B_OPCODE (16, 1, 0):	/* bca */
 | 
						||
	case B_OPCODE (16, 1, 1):	/* bcla */
 | 
						||
		if (!get_operand_value (op, instr, O_BD, &addr) ||
 | 
						||
			!get_operand_value (op, instr, O_BO, &bo) ||
 | 
						||
			!get_operand_value (op, instr, O_BI, &bi) ||
 | 
						||
			!get_operand_value (op, instr, O_AA, &aa) ||
 | 
						||
			!get_operand_value (op, instr, O_LK, &lk))
 | 
						||
			return false;
 | 
						||
 | 
						||
		if ((addr & (1 << 13)) != 0)
 | 
						||
			addr = addr - (1 << 14);
 | 
						||
		addr <<= 2;
 | 
						||
		conditional = 1;
 | 
						||
		branch = 1;
 | 
						||
		break;
 | 
						||
 | 
						||
	case I_OPCODE (18, 0, 0):	/* b */
 | 
						||
	case I_OPCODE (18, 0, 1):	/* bl */
 | 
						||
	case I_OPCODE (18, 1, 0):	/* ba */
 | 
						||
	case I_OPCODE (18, 1, 1):	/* bla */
 | 
						||
		if (!get_operand_value (op, instr, O_LI, &addr) ||
 | 
						||
			!get_operand_value (op, instr, O_AA, &aa) ||
 | 
						||
			!get_operand_value (op, instr, O_LK, &lk))
 | 
						||
			return false;
 | 
						||
 | 
						||
		if ((addr & (1 << 23)) != 0)
 | 
						||
			addr = addr - (1 << 24);
 | 
						||
		addr <<= 2;
 | 
						||
		conditional = 0;
 | 
						||
		branch = 1;
 | 
						||
		break;
 | 
						||
 | 
						||
	case XL_OPCODE (19, 528, 0):	/* bcctr */
 | 
						||
	case XL_OPCODE (19, 528, 1):	/* bcctrl */
 | 
						||
		if (!get_operand_value (op, instr, O_BO, &bo) ||
 | 
						||
			!get_operand_value (op, instr, O_BI, &bi) ||
 | 
						||
			!get_operand_value (op, instr, O_LK, &lk))
 | 
						||
			return false;
 | 
						||
 | 
						||
		addr = ctr;
 | 
						||
		aa = 1;
 | 
						||
		conditional = 1;
 | 
						||
		branch = 1;
 | 
						||
		break;
 | 
						||
 | 
						||
	case XL_OPCODE (19, 16, 0):	/* bclr */
 | 
						||
	case XL_OPCODE (19, 16, 1):	/* bclrl */
 | 
						||
		if (!get_operand_value (op, instr, O_BO, &bo) ||
 | 
						||
			!get_operand_value (op, instr, O_BI, &bi) ||
 | 
						||
			!get_operand_value (op, instr, O_LK, &lk))
 | 
						||
			return false;
 | 
						||
 | 
						||
		addr = lr;
 | 
						||
		aa = 1;
 | 
						||
		conditional = 1;
 | 
						||
		branch = 1;
 | 
						||
		break;
 | 
						||
 | 
						||
	default:
 | 
						||
		conditional = 0;
 | 
						||
		branch = 0;
 | 
						||
		break;
 | 
						||
	}
 | 
						||
 | 
						||
	if (conditional) {
 | 
						||
		switch ((bo & 0x1e) >> 1) {
 | 
						||
		case 0:				/* 0000y */
 | 
						||
			if (--ctr != 0)
 | 
						||
				ctr_ok = 1;
 | 
						||
 | 
						||
			cond_ok = !(cr & (1 << (31 - bi)));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 1:				/* 0001y */
 | 
						||
			if (--ctr == 0)
 | 
						||
				ctr_ok = 1;
 | 
						||
 | 
						||
			cond_ok = !(cr & (1 << (31 - bi)));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 2:				/* 001zy */
 | 
						||
			ctr_ok = 1;
 | 
						||
			cond_ok = !(cr & (1 << (31 - bi)));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 4:				/* 0100y */
 | 
						||
			if (--ctr != 0)
 | 
						||
				ctr_ok = 1;
 | 
						||
 | 
						||
			cond_ok = cr & (1 << (31 - bi));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 5:				/* 0101y */
 | 
						||
			if (--ctr == 0)
 | 
						||
				ctr_ok = 1;
 | 
						||
 | 
						||
			cond_ok = cr & (1 << (31 - bi));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 6:				/* 011zy */
 | 
						||
			ctr_ok = 1;
 | 
						||
			cond_ok = cr & (1 << (31 - bi));
 | 
						||
			break;
 | 
						||
 | 
						||
		case 8:				/* 1z00y */
 | 
						||
			if (--ctr != 0)
 | 
						||
				ctr_ok = cond_ok = 1;
 | 
						||
			break;
 | 
						||
 | 
						||
		case 9:				/* 1z01y */
 | 
						||
			if (--ctr == 0)
 | 
						||
				ctr_ok = cond_ok = 1;
 | 
						||
			break;
 | 
						||
 | 
						||
		case 10:				/* 1z1zz */
 | 
						||
			ctr_ok = cond_ok = 1;
 | 
						||
			break;
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if (branch && (!conditional || (ctr_ok && cond_ok))) {
 | 
						||
		if (aa)
 | 
						||
			step = addr;
 | 
						||
		else
 | 
						||
			step = addr + pc;
 | 
						||
 | 
						||
		if (lk)
 | 
						||
			next = pc + 4;
 | 
						||
		else
 | 
						||
			next = step;
 | 
						||
	} else {
 | 
						||
		step = next = pc + 4;
 | 
						||
	}
 | 
						||
 | 
						||
	if (step_over == true)
 | 
						||
		*(unsigned long *) nextaddr = next;
 | 
						||
	else
 | 
						||
		*(unsigned long *) nextaddr = step;
 | 
						||
 | 
						||
	return true;
 | 
						||
}								/* find_next_address */
 | 
						||
 | 
						||
 | 
						||
/*
 | 
						||
 * Copyright (c) 2000 William L. Pitts and W. Gerald Hicks
 | 
						||
 * All rights reserved.
 | 
						||
 *
 | 
						||
 * Redistribution and use in source and binary forms are freely
 | 
						||
 * permitted provided that the above copyright notice and this
 | 
						||
 * paragraph and the following disclaimer are duplicated in all
 | 
						||
 * such forms.
 | 
						||
 *
 | 
						||
 * This software is provided "AS IS" and without any express or
 | 
						||
 * implied warranties, including, without limitation, the implied
 | 
						||
 * warranties of merchantability and fitness for a particular
 | 
						||
 * purpose.
 | 
						||
 */
 |