|
@@ -0,0 +1,1256 @@
|
|
|
|
+/* $Id$ */
|
|
|
|
+
|
|
|
|
+#include <common.h>
|
|
|
|
+
|
|
|
|
+#if (CONFIG_COMMANDS & CFG_CMD_BEDBUG)
|
|
|
|
+
|
|
|
|
+#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.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#endif /* CONFIG_COMMANDS & CFG_CMD_BEDBUG */
|