Skip to content
Snippets Groups Projects
bedbug.c 29.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    /* $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,
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				enum OP_FIELD, unsigned long *));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    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 *,
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			struct operand *, char *, int *));
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    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.
     *
    
    York Sun's avatar
    York Sun committed
     * Returns true if the area was successfully disassembled or false if
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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) {
    
    York Sun's avatar
    York Sun committed
    			if ((line_info_from_addr ((Elf32_Word) ctx.virtual,
    				filename, funcname, &line_no) == true) &&
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				((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 =
    
    York Sun's avatar
    York Sun committed
    				 symbol_name_from_addr((Elf32_Word) ctx.virtual,
    						true, 0)) != 0) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    				cursym = symname;
    				symoffset = 0;
    			} else {
    				if ((cursym == 0) &&
    					((symname =
    
    York Sun's avatar
    York Sun committed
    					  symbol_name_from_addr((Elf32_Word) ctx.virtual,
    						false, &symoffset)) != 0)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    					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) ||
    
    York Sun's avatar
    York Sun committed
    			(ctx.op->hfunc == 0) ||
    			((*ctx.op->hfunc) (&ctx) == false)) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    			sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name);
    			ctx.datalen += 8;
    			print_operands (&ctx);
    		}
    
    		(*pfunc) (ctx.data);
    	}
    
    
    York Sun's avatar
    York Sun committed
    	return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }								/* 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
    
    York Sun's avatar
    York Sun committed
     *			true.  This may be passed as 0 if the value is
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     *			not required.
     *
    
    York Sun's avatar
    York Sun committed
     * Returns true if the operand was found or false if it was not.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    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) {
    
    York Sun's avatar
    York Sun committed
    		return false;			/* bad operand ?! */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    	/* 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);
    		}
    
    York Sun's avatar
    York Sun committed
    		return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    York Sun's avatar
    York Sun committed
    	return false;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }								/* 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.
     *
    
    York Sun's avatar
    York Sun committed
     * Returns true if the simpler form was printed or false if it was not.
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    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
    	};
    
      /*------------------------------------------------------------*/
    
    
    York Sun's avatar
    York Sun committed
    	if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false)
    		return false;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    York Sun's avatar
    York Sun committed
    	if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false)
    		return false;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    	if ((bo == 12) && (bi == 0)) {
    		ctx->op = &blt;
    		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
    		ctx->datalen += 8;
    		print_operands (ctx);
    
    York Sun's avatar
    York Sun committed
    		return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	} else if ((bo == 4) && (bi == 10)) {
    		ctx->op = &bne;
    		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
    		ctx->datalen += 8;
    		print_operands (ctx);
    
    York Sun's avatar
    York Sun committed
    		return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	} else if ((bo == 16) && (bi == 0)) {
    		ctx->op = &bdnz;
    		sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name);
    		ctx->datalen += 8;
    		print_operands (ctx);
    
    York Sun's avatar
    York Sun committed
    		return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    York Sun's avatar
    York Sun committed
    	return false;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }								/* 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.
     *
     *
    
    York Sun's avatar
    York Sun committed
     * Returns true if it was able to output the line info, or false if it was
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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);
    
    
    York Sun's avatar
    York Sun committed
    	return true;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }								/* 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;