/* $Id$ */

#include <ppcboot.h>
#include <linux/ctype.h>
#include <bedbug/bedbug.h>
#include <bedbug/ppc.h>
#include <bedbug/regs.h>
#include <bedbug/tables.h>

#define Elf32_Word	unsigned long

/* USE_SOURCE_CODE enables some symbolic debugging functions of this
   code.  This is only useful if the program will have access to the
   source code for the binary being examined.
*/

/* #define USE_SOURCE_CODE 1 */

#ifdef USE_SOURCE_CODE
extern int line_info_from_addr __P((Elf32_Word, char *, char *, int *));
extern struct symreflist *symByAddr;
extern char *symbol_name_from_addr __P((Elf32_Word,int,int *));
#endif /* USE_SOURCE_CODE */

int		print_operands __P((struct ppc_ctx *));
int		get_operand_value __P(( struct opcode *, unsigned long,
					enum OP_FIELD, unsigned long * ));
struct opcode *	find_opcode __P((unsigned long));
struct opcode * find_opcode_by_name __P((char *));
char *		spr_name __P((int));
int		spr_value __P((char *));
char *		tbr_name __P((int));
int		tbr_value __P((char *));
int		parse_operand __P((unsigned long,struct opcode *,
				   struct operand *,char *,int *));
int		get_word __P((char**,char*));
long		read_number __P((char *));
int		downstring __P((char*));


/*======================================================================
 * Entry point for the PPC disassembler.
 *
 * Arguments:
 *	memaddr		The address to start disassembling from.
 *
 *	virtual		If this value is non-zero, then this will be 
 *			used as the base address for the output and
 *			symbol lookups.  If this value is zero then
 *			memaddr is used as the absolute address.
 *
 *	num_instr	The number of instructions to disassemble.  Since
 *			each instruction is 32 bits long, this can be
 *			computed if you know the total size of the region.
 *
 *	pfunc		The address of a function that is called to print
 *			each line of output.  The function should take a
 *			single character pointer as its parameters a la puts.
 *
 *	flags		Sets options for the output.  This is a
 *			bitwise-inclusive-OR of the following
 *			values.  Note that only one of the radix
 *			options may be set.
 *
 *			F_RADOCTAL	- output radix is unsigned base 8.
 *			F_RADUDECIMAL	- output radix is unsigned base 10.
 *			F_RADSDECIMAL	- output radix is signed base 10.
 *			F_RADHEX	- output radix is unsigned base 16.
 *			F_SIMPLE	- use simplified mnemonics.
 *			F_SYMBOL	- lookup symbols for addresses.
 *			F_INSTR		- output raw instruction.
 *			F_LINENO	- show line # info if available.
 *
 * Returns TRUE if the area was successfully disassembled or FALSE if
 * a problem was encountered with accessing the memory.
 */

int disppc( unsigned char *memaddr, unsigned char *virtual, int num_instr,
	    int (*pfunc)(const char *), unsigned long flags )
{
  int			i;
  struct ppc_ctx	ctx;
#ifdef USE_SOURCE_CODE
  int			line_no = 0;
  int			last_line_no = 0;
  char			funcname[ 128 ] = {0};
  char			filename[ 256 ] = {0};
  char			last_funcname[ 128 ] = {0};
  int			symoffset;
  char *		symname;
  char *		cursym = (char *)0;
#endif /* USE_SOURCE_CODE */
  /*------------------------------------------------------------*/

  ctx.flags   = flags;
  ctx.virtual = virtual;
  
  /* Figure out the output radix before we go any further */
  
  if( ctx.flags & F_RADOCTAL )
    {
      /* Unsigned octal output */
      strcpy( ctx.radix_fmt, "O%o" );
    }
  else if( ctx.flags & F_RADUDECIMAL )
    {
      /* Unsigned decimal output */
      strcpy( ctx.radix_fmt, "%u" );
    }
  else if( ctx.flags & F_RADSDECIMAL )
    {
      /* Signed decimal output */
      strcpy( ctx.radix_fmt, "%d" );
    }
  else
    {
      /* Unsigned hex output */
      strcpy( ctx.radix_fmt, "0x%x" );
    }

  if( ctx.virtual == 0 )
    {
      ctx.virtual = memaddr;
    }

#ifdef USE_SOURCE_CODE
  if( ctx.flags & F_SYMBOL )
    {
      if( symByAddr == 0 )	/* no symbols loaded */
	ctx.flags &= ~F_SYMBOL;
      else
	{
	  cursym = (char *)0;
	  symoffset = 0;
	}
    }
#endif /* USE_SOURCE_CODE */
	  
  /* format each line as "XXXXXXXX: <symbol> IIIIIIII  disassembly" where,
     XXXXXXXX is the memory address in hex,
     <symbol> is the symbolic location if F_SYMBOL is set.
     IIIIIIII is the raw machine code in hex if F_INSTR is set,
     and disassembly is the disassembled machine code with numbers
     formatted according to the 'radix' parameter */
  
  for( i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4 )
    {
#ifdef USE_SOURCE_CODE
      if( ctx.flags & F_LINENO )
	{
	  if(( line_info_from_addr( (Elf32_Word) ctx.virtual, filename,
				    funcname, &line_no ) == TRUE ) &&
	     (( line_no != last_line_no ) ||
	      ( strcmp( last_funcname, funcname ) != 0 )))
	    {
	      print_source_line( filename, funcname, line_no, pfunc );
	    }
	  last_line_no = line_no;
	  strcpy( last_funcname, funcname );
	}
#endif /* USE_SOURCE_CODE */

      sprintf( ctx.data, "%08lx: ", (unsigned long)ctx.virtual );
      ctx.datalen = 10;

#ifdef USE_SOURCE_CODE
      if( ctx.flags & F_SYMBOL )
	{
	  if(( symname =
	       symbol_name_from_addr( (Elf32_Word)ctx.virtual,
				      TRUE, 0 )) != 0 )
	    {
	      cursym = symname;
	      symoffset = 0;
	    }
	  else
	    {
	      if( (cursym == 0) &&
		  ((symname =
		    symbol_name_from_addr( (Elf32_Word)ctx.virtual,
					   FALSE, &symoffset )) != 0 ))
		{
		  cursym = symname;
		}
	      else
		{
		  symoffset += 4;
		}
	    }
	  
	  if( cursym != 0 )
	    {
	      sprintf( &ctx.data[ ctx.datalen ], "<%s+", cursym );
	      ctx.datalen = strlen( ctx.data );
	      sprintf( &ctx.data[ ctx.datalen ], ctx.radix_fmt, symoffset );
	      strcat( ctx.data, ">" );
	      ctx.datalen = strlen( ctx.data );
	    }
	}
#endif /* USE_SOURCE_CODE */
      
      ctx.instr = INSTRUCTION( memaddr );

      if( ctx.flags & F_INSTR )
	{
	  /* Find the opcode structure for this opcode.  If one is not found
	     then it must be an illegal instruction */
	  sprintf( &ctx.data[ ctx.datalen ], "   %02lx %02lx %02lx %02lx    ",
		   ((ctx.instr >> 24) & 0xff), ((ctx.instr >> 16) & 0xff),
		   ((ctx.instr >> 8) & 0xff), (ctx.instr & 0xff));
	  ctx.datalen += 18;
	}
      else
	{
	  strcat( ctx.data, "   " );
	  ctx.datalen += 3;
	}

      if(( ctx.op = find_opcode( ctx.instr )) == 0 )
	{
	  /* Illegal Opcode */
	  sprintf( &ctx.data[ ctx.datalen ], "        .long 0x%08lx",
		   ctx.instr );
	  ctx.datalen += 24;
	  (*pfunc)( ctx.data );
	  continue;
	}

      if((( ctx.flags & F_SIMPLE ) == 0 ) ||
	 ( ctx.op->hfunc == 0 ) ||
	 ((*ctx.op->hfunc)( &ctx ) == FALSE ))
	{
	  sprintf( &ctx.data[ ctx.datalen ], "%-7s ", ctx.op->name );
	  ctx.datalen += 8;
	  print_operands( &ctx );
	}

      (*pfunc)( ctx.data );
    }

  return TRUE;
} /* disppc */



/*======================================================================
 * Called by the disassembler to print the operands for an instruction.
 *
 * Arguments:
 *	ctx		A pointer to the disassembler context record.
 *
 * always returns 0.
 */

int print_operands( struct ppc_ctx *ctx )
{
  int			open_parens = 0;
  int			field;
  unsigned long 	operand;
  struct operand *	opr;
#ifdef USE_SOURCE_CODE
  char *		symname;
  int			offset;
#endif /* USE_SOURCE_CODE */
  /*------------------------------------------------------------*/

  /* Walk through the operands and list each in order */
  for( field = 0; ctx->op->fields[ field ] != 0; ++field )
    {
      if( ctx->op->fields[ field ] > n_operands )
	{
	  continue;	/* bad operand ?! */
	}
	  
      opr = &operands[ ctx->op->fields[ field ] - 1 ];

      if( opr->hint & OH_SILENT )
	{
	  continue;
	}

      if(( field > 0 ) && !open_parens )
	{
	  strcat( ctx->data, "," );
	  ctx->datalen++;
	}

      operand = (ctx->instr >> opr->shift) & ((1 << opr->bits)-1);

      if( opr->hint & OH_ADDR )
	{
	  if(( operand & (1 << (opr->bits-1))) != 0 )
	    {
	      operand = operand - (1 << opr->bits);
	    }

	  if( ctx->op->hint & H_RELATIVE )
	    operand = (operand << 2) + (unsigned long)ctx->virtual;
	  else
	    operand = (operand << 2);
	    

	  sprintf( &ctx->data[ ctx->datalen ], "0x%lx", operand );
	  ctx->datalen = strlen( ctx->data );

#ifdef USE_SOURCE_CODE
	  if(( ctx->flags & F_SYMBOL ) &&
	     (( symname = symbol_name_from_addr( operand, 0, &offset )) != 0 ))
	    {
	      sprintf( &ctx->data[ ctx->datalen ], " <%s", symname );
	      if( offset != 0 )
		{
		  strcat( ctx->data, "+" );
		  ctx->datalen = strlen( ctx->data );
		  sprintf( &ctx->data[ ctx->datalen ], ctx->radix_fmt,
			   offset );
		}
	      strcat( ctx->data, ">" );
	    }
#endif /* USE_SOURCE_CODE */
	}

      else if( opr->hint & OH_REG )
	{
	  if(( operand == 0 )       &&
	     ( opr->field == O_rA ) &&
	     ( ctx->op->hint & H_RA0_IS_0 ))
	    {
	      strcat( ctx->data, "0" );
	    }
	  else
	    {
	      sprintf( &ctx->data[ ctx->datalen ], "r%d", (short)operand );
	    }

	  if( open_parens )
	    {
	      strcat( ctx->data, ")" );
	      open_parens--;
	    }
	}

      else if( opr->hint & OH_SPR )
	{
	  strcat( ctx->data, spr_name( operand ));
	}

      else if( opr->hint & OH_TBR )
	{
	  strcat( ctx->data, tbr_name( operand ));
	}

      else if( opr->hint & OH_LITERAL )
	{
	  switch( opr->field )
	    {
	    case O_cr2:
	      strcat( ctx->data, "cr2" );
	      ctx->datalen += 3;
	      break;

	    default:
	      break;
	    }
	}
      
      else
	{
	  sprintf( &ctx->data[ ctx->datalen ], ctx->radix_fmt,
		   (unsigned short)operand );

	  if( open_parens )
	    {
	      strcat( ctx->data, ")" );
	      open_parens--;
	    }

	  else if( opr->hint & OH_OFFSET )
	    {
	      strcat( ctx->data, "(" );
	      open_parens++;
	    }
	}

      ctx->datalen = strlen( ctx->data );
    }

  return 0;
} /* print_operands */



/*======================================================================
 * Called to get the value of an arbitrary operand with in an instruction.
 *
 * Arguments:
 *	op		The pointer to the opcode structure to which
 *			the operands belong.
 *
 *	instr		The instruction (32 bits) containing the opcode
 *			and the operands to print.  By the time that
 *			this routine is called the operand has already
 *			been added to the output.
 *
 *	field		The field (operand) to get the value of.
 *
 *	value		The address of an unsigned long to be filled in
 *			with the value of the operand if it is found.  This
 *			will only be filled in if the function returns
 *			TRUE.  This may be passed as 0 if the value is
 *			not required.
 *
 * Returns TRUE if the operand was found or FALSE if it was not.
 */

int get_operand_value( struct opcode *op, unsigned long instr,
		       enum OP_FIELD field, unsigned long *value )
{
  int			i;
  struct operand *	opr;
  /*------------------------------------------------------------*/

  if( field > n_operands )
    {
      return FALSE;	/* bad operand ?! */
    }

  /* Walk through the operands and list each in order */
  for( i = 0; op->fields[ i ] != 0; ++i )
    {
      if( op->fields[ i ] != field )
	{
	  continue;
	}

      opr = &operands[ op->fields[ i ] - 1 ];

      if( value )
	{
	  *value = (instr >> opr->shift) & ((1 << opr->bits)-1);
	}
      return TRUE;
    }

  return FALSE;
} /* operand_value */



/*======================================================================
 * Called by the disassembler to match an opcode value to an opcode structure.
 *
 * Arguments:
 *	instr		The instruction (32 bits) to match.  This value
 *			may contain operand values as well as the opcode
 *			since they will be masked out anyway for this
 *			search.
 *
 * Returns the address of an opcode struct (from the opcode table) if the
 * operand successfully matched an entry, or 0 if no match was found.
 */

struct opcode *find_opcode( unsigned long instr )
{
  struct opcode *	ptr;
  int			top = 0;
  int			bottom = n_opcodes - 1;
  int			idx;
  /*------------------------------------------------------------*/

  while( top <= bottom )
    {
      idx = (top + bottom) >> 1;
      ptr = &opcodes[ idx ];
      
      if(( instr & ptr->mask ) < ptr->opcode )
	{
	  bottom = idx - 1;
	}
      else if(( instr & ptr->mask ) > ptr->opcode )
	{
	  top = idx + 1;
	}
      else
	{
	  return ptr;
	}
    }

  return (struct opcode *)0;
} /* find_opcode */



/*======================================================================
 * Called by the assembler to match an opcode name to an opcode structure.
 *
 * Arguments:
 *	name		The text name of the opcode, e.g. "b", "mtspr", etc.
 *
 * The opcodes are sorted numerically by their instruction binary code
 * so a search for the name cannot use the binary search used by the
 * other find routine.
 *
 * Returns the address of an opcode struct (from the opcode table) if the
 * name successfully matched an entry, or 0 if no match was found.
 */

struct opcode *find_opcode_by_name( char *name )
{
  int			idx;
  /*------------------------------------------------------------*/

  downstring( name );

  for( idx = 0; idx < n_opcodes; ++idx )
    {
      if( !strcmp( name, opcodes[ idx ].name ))
	return &opcodes[ idx ];
    }

  return (struct opcode *)0;
} /* find_opcode_by_name */



/*======================================================================
 * Convert the 'spr' operand from its numeric value to its symbolic name.
 *
 * Arguments:
 *	value		The value of the 'spr' operand.  This value should
 *			be unmodified from its encoding in the instruction.
 *			the split-field computations will be performed
 *			here before the switch.
 *
 * Returns the address of a character array containing the name of the
 * special purpose register defined by the 'value' parameter, or the
 * address of a character array containing "???" if no match was found.
 */

char *spr_name( int value )
{
  unsigned short	spr;
  static char		other[ 10 ];
  int			i;
  /*------------------------------------------------------------*/
  
  /* spr is a 10 bit field whose interpretation has the high and low
     five-bit fields reversed from their encoding in the operand */
  
  spr = (( value >> 5 ) & 0x1f ) | (( value & 0x1f ) << 5 );

  for( i = 0; i < n_sprs; ++i ){
    if( spr == spr_map[ i ].spr_val )
      return spr_map[ i ].spr_name;
  }
  
  sprintf( other, "%d", spr );
  return other;
} /* spr_name */
     


/*======================================================================
 * Convert the 'spr' operand from its symbolic name to its numeric value
 *
 * Arguments:
 *	name		The symbolic name of the 'spr' operand.  The
 *			split-field encoding will be done by this routine.
 *			NOTE: name can be a number.
 *
 * Returns the numeric value for the spr appropriate for encoding a machine
 * instruction.  Returns 0 if unable to find the SPR.
 */

int spr_value( char *name )
{
  struct spr_info *	sprp;
  int			spr;
  int			i;
  /*------------------------------------------------------------*/

  if( !name || !*name )
    return 0;
  
  if( isdigit((int)name[ 0 ])) {
    i = htonl( read_number( name ));
    spr = (( i >> 5 ) & 0x1f ) | (( i & 0x1f ) << 5 );
    return spr;
  }

  downstring( name );
  
  for( i = 0; i < n_sprs; ++i ) {
    sprp = &spr_map[ i ];
    
    if( strcmp( name, sprp->spr_name ) == 0 ) {
      /* spr is a 10 bit field whose interpretation has the high and low
	 five-bit fields reversed from their encoding in the operand */
      i = htonl( sprp->spr_val );
      spr = (( i >> 5 ) & 0x1f ) | (( i & 0x1f ) << 5 );

      return spr;
    }
  }

  return 0;
} /* spr_value */
     


/*======================================================================
 * Convert the 'tbr' operand from its numeric value to its symbolic name.
 *
 * Arguments:
 *	value		The value of the 'tbr' operand.  This value should
 *			be unmodified from its encoding in the instruction.
 *			the split-field computations will be performed
 *			here before the switch.
 *
 * Returns the address of a character array containing the name of the
 * time base register defined by the 'value' parameter, or the address
 * of a character array containing "???" if no match was found.
 */

char *tbr_name( int value )
{
  unsigned short	tbr;
  /*------------------------------------------------------------*/
  
  /* tbr is a 10 bit field whose interpretation has the high and low
     five-bit fields reversed from their encoding in the operand */
  
  tbr = (( value >> 5 ) & 0x1f ) | (( value & 0x1f ) << 5 );

  if( tbr == 268 )
    return "TBL";
  
  else if( tbr == 269 )
    return "TBU";
  

  return "???";
} /* tbr_name */



/*======================================================================
 * Convert the 'tbr' operand from its symbolic name to its numeric value.
 *
 * Arguments:
 *	name		The symbolic name of the 'tbr' operand.  The
 *			split-field encoding will be done by this routine.
 *
 * Returns the numeric value for the spr appropriate for encoding a machine
 * instruction.  Returns 0 if unable to find the TBR.
 */

int tbr_value( char *name )
{
  int	tbr;
  int	val;
  /*------------------------------------------------------------*/

  if( !name || !*name )
    return 0;

  downstring( name );
  
  if( isdigit((int)name[ 0 ])) {
    val = read_number( name );

    if( val != 268 && val != 269 )
      return 0;
  }
  else if( strcmp( name, "tbl" ) == 0 )
    val = 268;
  else if( strcmp( name, "tbu" ) == 0 )
    val = 269;
  else
    return 0;
  
  /* tbr is a 10 bit field whose interpretation has the high and low
     five-bit fields reversed from their encoding in the operand */

  val = htonl( val );
  tbr = (( val >> 5 ) & 0x1f ) | (( val & 0x1f ) << 5 );
  return tbr;
} /* tbr_name */



/*======================================================================
 * The next several functions (handle_xxx) are the routines that handle
 * disassembling the opcodes with simplified mnemonics.
 *
 * Arguments:
 *	ctx		A pointer to the disassembler context record.
 *
 * Returns TRUE if the simpler form was printed or FALSE if it was not.
 */

int handle_bc( struct ppc_ctx *ctx )
{
  unsigned long	bo;
  unsigned long	bi;
  static struct opcode	blt  = {B_OPCODE(16,0,0), B_MASK, {O_BD, 0},
				0, "blt", H_RELATIVE};
  static struct opcode	bne  = {B_OPCODE(16,0,0), B_MASK, {O_cr2, O_BD, 0},
				0, "bne", H_RELATIVE};
  static struct opcode	bdnz = {B_OPCODE(16,0,0), B_MASK, {O_BD, 0},
				0, "bdnz", H_RELATIVE};
  /*------------------------------------------------------------*/

  if( get_operand_value( ctx->op, ctx->instr, O_BO, &bo ) == FALSE )
    return FALSE;
  
  if( get_operand_value( ctx->op, ctx->instr, O_BI, &bi ) == FALSE )
    return FALSE;

  if(( bo == 12 ) && ( bi == 0 ))
    {
      ctx->op = &blt;
      sprintf( &ctx->data[ ctx->datalen ], "%-7s ", ctx->op->name );
      ctx->datalen += 8;
      print_operands( ctx );
      return TRUE;
    }
  else if(( bo == 4 ) && ( bi == 10 ))
    {
      ctx->op = &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 */



/*======================================================================
 * 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 )) == 0 )
      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 0;
    }

    data = htonl( data );
  }

  else if( oper->hint & OH_SPR ) {
    if(( data = spr_value( txt )) == 0 ) {
      if( err ) *err = E_ASM_BAD_SPR;
      return 0;
    }
  }

  else if( oper->hint & OH_TBR ) {
    if(( data = tbr_value( txt )) == 0 ) {
      if( err ) *err = E_ASM_BAD_TBR;
      return 0;
    }
  }

  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 ))
    dest[ nchars++ ] = *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.
 */
