/* Instruction printing code for the Alpha
   Copyright (C) 1993, 1995 Free Software Foundation, Inc.
   Contributed by Cygnus Support.

Written by Steve Chamberlain (sac@cygnus.com)

This file is part of libopcodes.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
This program; if not, write to the Free Software Foundation, Inc., 675
 Mass Ave, Boston, MA 02111-1307, USA.
*/

#include "dis-asm.h"
#define DEFINE_TABLE
#include "alpha-opc.h"


/* Print one instruction from PC on INFO->STREAM.
   Return the size of the instruction (always 4 on alpha). */

int
print_insn_alpha(pc, info)
     bfd_vma pc;
     struct disassemble_info *info;
{
  alpha_insn	*insn;
  unsigned char	b[4];
  void		*stream = info->stream;
  fprintf_ftype	func = info->fprintf_func;
  unsigned long	given;
  int		status ;
  int found = 0;

  status = (*info->read_memory_func) (pc, (bfd_byte *) &b[0], 4, info);
  if (status != 0) {
    (*info->memory_error_func) (status, pc, info);
    return -1;
  }
  given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);

  func (stream, "\t%08x\t", given);

  for (insn = alpha_insn_set;
       insn->name && !found;
       insn++)
    {
      switch (insn->type)
	{
	case MEMORY_FORMAT_CODE:
	  if ((insn->i & MEMORY_FORMAT_MASK)
	      ==(given & MEMORY_FORMAT_MASK))
	    {
	      func (stream, "%s\t%s, %d(%s)",
		    insn->name,
		    alpha_regs[RA(given)],
		    OPCODE (given) == 9 ? DISP(given) * 65536 : DISP(given),
		    alpha_regs[RB(given)]);
	      found = 1;
	    }
	  break;


	case MEMORY_FUNCTION_FORMAT_CODE:
	  if ((insn->i & MEMORY_FUNCTION_FORMAT_MASK)
	      ==(given & MEMORY_FUNCTION_FORMAT_MASK))
	    {
	      switch (given & 0xffff)
		{
		case 0x8000: /* fetch */
		case 0xa000: /* fetch_m */
		  func (stream, "%s\t(%s)", insn->name, alpha_regs[RB(given)]);
		  break;

		case 0xc000: /* rpcc */
		  func (stream, "%s\t%s", insn->name, alpha_regs[RA(given)]);
		  break;

		default:
		  func (stream, "%s", insn->name);
		  break;
		}
	      found = 1;
	    }
	  break;

	case BRANCH_FORMAT_CODE:
	  if ((insn->i & BRANCH_FORMAT_MASK)
	      == (given & BRANCH_FORMAT_MASK))
	    {
	      if (RA(given) == 31)
		func (stream, "%s\t ", insn->name);
	      else
		func (stream, "%s\t%s, ", insn->name,
		      alpha_regs[RA(given)]);
	      (*info->print_address_func) (BDISP(given) * 4 + pc + 4, info);
	      found = 1;
	    }
	  break;

	case MEMORY_BRANCH_FORMAT_CODE:
	  if ((insn->i & MEMORY_BRANCH_FORMAT_MASK)
	      == (given & MEMORY_BRANCH_FORMAT_MASK))
	    {
	      if (given & (1<<15))
		{
		  func (stream, "%s\t%s, (%s), %d", insn->name,
			alpha_regs[RA(given)],
			alpha_regs[RB(given)],
			JUMP_HINT(given));
		}
	      else
		{
		  /* The displacement is a hint only, do not put out
		     a symbolic address.  */
		  func (stream, "%s\t%s, (%s), 0x%lx", insn->name,
			alpha_regs[RA(given)],
			alpha_regs[RB(given)],
			JDISP(given) * 4 + pc + 4);
		}
	      found = 1;
	    }
	  break;

	case OPERATE_FORMAT_CODE:
	  if ((insn->i & OPERATE_FORMAT_MASK)
	      == (given & OPERATE_FORMAT_MASK))
	    {
	      int opcode = OPCODE(given);
	      int optype = OP_OPTYPE(given);
	      if (OP_OPTYPE(insn->i) == optype)
		{
		  int ra;
		  ra = RA(given);

		  if (OP_IS_CONSTANT(given))
		    {
		      if ((opcode == 0x11) && (optype == 0x20)
			  && (ra == 31))	  /* bis R31, lit, Ry */
			{
			  func (stream, "mov\t0x%x, %s",
				LITERAL(given), alpha_regs[RC(given)] );
			}
		      else
			{
#if GNU_ASMCODE
			  func (stream, "%s\t%s, 0x%x, %s", insn->name,
				alpha_regs[RA(given)],
				LITERAL(given),
				alpha_regs[RC(given)]);
#else
			  func (stream, "%s\t%s, #%d, %s", insn->name,
				alpha_regs[RA(given)],
				LITERAL(given),
				alpha_regs[RC(given)]);
			}
#endif
		  } else {		/* not constant */
		    int rb, rc;
		    rb = RB(given); rc = RC(given);
		    switch ((opcode << 8) | optype)
		      {
		      case 0x1009:			/* subl */
			if (ra == 31)
			  {
			    func (stream, "negl\t%s, %s",
				  alpha_regs[rb], alpha_regs[rc]);
			    found = 1;
			  }
			break;
		      case 0x1029:			/* subq */
			if (ra == 31)
			  {
			    func (stream, "negq\t%s, %s",
				  alpha_regs[rb], alpha_regs[rc]);
			    found = 1;
			  }
			break;
		      case 0x1120:			/* bis */
			if (ra == 31)
			  {
			    if (ra == rb)		/* ra=R31, rb=R31 */
			      {
				if (rc == 31)
				  func (stream, "nop");
				else
				  func (stream, "clr\t%s", alpha_regs[rc]);
			      }
			    else
			      func (stream, "mov\t%s, %s",
				    alpha_regs[rb], alpha_regs[rc]);
			  }
			else
			  func (stream, "or\t%s, %s, %s",
				alpha_regs[ra], alpha_regs[rb],
				alpha_regs[rc]);
			found = 1;
			break;

		      default:
			break;

		      }

		    if (!found)
		      func (stream, "%s\t%s, %s, %s", insn->name,
			    alpha_regs[ra], alpha_regs[rb], alpha_regs[rc]);
		  }
		  found = 1;
		}
	    }

	  break;

	case FLOAT_FORMAT_CODE:
	  if ((insn->i & FLOAT_FORMAT_MASK)
	      == (given & FLOAT_FORMAT_MASK))
	    {
	      int ra, rb, rc;
	      ra = RA(given); rb = RB(given); rc = RC(given);
	      switch (OP_OPTYPE(given))
		{
		case 0x20:		/* cpys */
		  if (ra == 31)
		    {
		      if (rb == 31)
			{
			  if (rc == 31)
			    func (stream, "fnop");
			  else
			    func (stream, "fclr\tf%d", rc);
			}
		      else
			func (stream, "fabs\tf%d, f%d", rb, rc);
		      found = 1;
		    }
		  else if (ra == rb)
		    {
		      func (stream, "fmov\tf%d, f%d", rb, rc);
		      found = 1;
		    }
		  break;
		case 0x21:		/* cpysn */
		  if (ra == rb)
		    {
		      func (stream, "fneg\tf%d, f%d", rb, rc);
		      found = 1;
		    }
		default:
		  ;
	        }

	      if (!found)
	        func (stream, "%s\tf%d, f%d, f%d", insn->name, ra, rb, rc);

	      found = 1;
	    }

	  break;
	case PAL_FORMAT_CODE:
	  if (insn->i == given)
	    {
	      func (stream, "call_pal %s", insn->name);
	      found = 1;
	    }

	  break;
	case FLOAT_MEMORY_FORMAT_CODE:
	  if ((insn->i & MEMORY_FORMAT_MASK)
	      ==(given & MEMORY_FORMAT_MASK))
	    {
	      func (stream, "%s\tf%d, %d(%s)",
		      insn->name,
		      RA(given),
		      OPCODE (given) == 9 ? DISP(given) * 65536 : DISP(given),
		      alpha_regs[RB(given)]);
	      found = 1;
	    }
	  break;
	case FLOAT_BRANCH_FORMAT_CODE:
	  if ((insn->i & BRANCH_FORMAT_MASK)
	      == (given & BRANCH_FORMAT_MASK))
	    {
	      func (stream, "%s\tf%d, ",
		    insn->name,
		    RA(given));
	      (*info->print_address_func) (BDISP(given) * 4 + pc + 4, info);
	      found = 1;
	    }
	  break;
	}
    }

  if (!found)
    switch (OPCODE (given))
      {
      case 0x00:
	func (stream, "call_pal 0x%x", given);
	break;
      case 0x19:
      case 0x1b:
      case 0x1d:
      case 0x1e:
      case 0x1f:
	func (stream, "PAL%X 0x%x", OPCODE (given), given & 0x3ffffff);
	break;
      case 0x01:
      case 0x02:
      case 0x03:
      case 0x04:
      case 0x05:
      case 0x06:
      case 0x07:
      case 0x0a:
      case 0x0c:
      case 0x0d:
      case 0x0e:
      case 0x14:
      case 0x1c:
	func (stream, "OPC%02X 0x%x", OPCODE (given), given & 0x3ffffff);
	break;
      }

  return 4;
}
