]> git.sur5r.net Git - u-boot/commitdiff
Initial revision
authorwdenk <wdenk>
Thu, 5 Sep 2002 16:50:46 +0000 (16:50 +0000)
committerwdenk <wdenk>
Thu, 5 Sep 2002 16:50:46 +0000 (16:50 +0000)
common/bedbug.c [new file with mode: 0644]

diff --git a/common/bedbug.c b/common/bedbug.c
new file mode 100644 (file)
index 0000000..fe54d17
--- /dev/null
@@ -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 *));
+\f
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 = &bne;
+               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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */
+\f
+
+
+/*======================================================================
+ * 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 */