/* pj-dis.c -- Disassemble picoJava instructions.
  Copyright (C) 1999-2024 Free Software Foundation, Inc.
  Contributed by Steve Chamberlain, of Transmeta ([email protected]).

  This file is part of the GNU opcodes library.

  This library 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 3, or (at your option)
  any later version.

  It 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., 51 Franklin Street - Fifth Floor, Boston,
  MA 02110-1301, USA.  */

#include "sysdep.h"
#include <stdio.h>
#include "libiberty.h"
#include "opcode/pj.h"
#include "disassemble.h"

extern const pj_opc_info_t pj_opc_info[512];

static int
get_int (bfd_vma memaddr, int *iptr, struct disassemble_info *info)
{
 unsigned char ival[4];
 int status = info->read_memory_func (memaddr, ival, 4, info);

 *iptr = (((unsigned) ival[0] << 24)
          | (ival[1] << 16)
          | (ival[2] << 8)
          | (ival[3] << 0));

 return status;
}

int
print_insn_pj (bfd_vma addr, struct disassemble_info *info)
{
 fprintf_ftype fprintf_fn = info->fprintf_func;
 void *stream = info->stream;
 unsigned char opcode;
 int status;

 if ((status = info->read_memory_func (addr, &opcode, 1, info)))
   goto fail;

 if (opcode == 0xff)
   {
     unsigned char byte_2;

     if ((status = info->read_memory_func (addr + 1, &byte_2, 1, info)))
       goto fail;
     fprintf_fn (stream, "%s", pj_opc_info[opcode + byte_2].u.name);
     return 2;
   }
 else
   {
     char *sep = "\t";
     int insn_start = addr;
     const pj_opc_info_t *op = &pj_opc_info[opcode];
     unsigned int a;

     addr++;
     fprintf_fn (stream, "%s", op->u.name);

     /* The tableswitch instruction is followed by the default
        address, low value, high value and the destinations.  */

     if (strcmp (op->u.name, "tableswitch") == 0)
       {
         int lowval;
         int highval;
         int val;

         addr = (addr + 3) & ~3;
         if ((status = get_int (addr, &val, info)))
           goto fail;

         fprintf_fn (stream, " default: ");
         (*info->print_address_func) (val + insn_start, info);
         addr += 4;

         if ((status = get_int (addr, &lowval, info)))
           goto fail;
         addr += 4;

         if ((status = get_int (addr, &highval, info)))
           goto fail;
         addr += 4;

         while (lowval <= highval)
           {
             if ((status = get_int (addr, &val, info)))
               goto fail;
             fprintf_fn (stream, " %d:[", lowval);
             (*info->print_address_func) (val + insn_start, info);
             fprintf_fn (stream, " ]");
             addr += 4;
             lowval++;
           }
         return addr - insn_start;
       }

     /* The lookupswitch instruction is followed by the default
        address, element count and pairs of values and
        addresses.  */
     if (strcmp (op->u.name, "lookupswitch") == 0)
       {
         int count;
         int val;

         addr = (addr + 3) & ~3;
         if ((status = get_int (addr, &val, info)))
           goto fail;
         addr += 4;

         fprintf_fn (stream, " default: ");
         (*info->print_address_func) (val + insn_start, info);

         if ((status = get_int (addr, &count, info)))
           goto fail;
         addr += 4;

         while (count--)
           {
             if ((status = get_int (addr, &val, info)))
               goto fail;
             addr += 4;
             fprintf_fn (stream, " %d:[", val);

             if ((status = get_int (addr, &val, info)))
               goto fail;
             addr += 4;

             (*info->print_address_func) (val + insn_start, info);
             fprintf_fn (stream, " ]");
           }
         return addr - insn_start;
       }

     for (a = 0; a < ARRAY_SIZE (op->arg) && op->arg[a]; a++)
       {
         unsigned char data[4];
         int val = 0;
         int i;
         int size = ASIZE (op->arg[a]);

         if ((status = info->read_memory_func (addr, data, size, info)))
           goto fail;

         val = (UNS (op->arg[0]) || ((data[0] & 0x80) == 0)) ? 0 : -1;

         for (i = 0; i < size; i++)
           val = ((unsigned) val << 8) | (data[i] & 0xff);

         fprintf_fn (stream, "%s", sep);
         if (PCREL (op->arg[a]))
           (*info->print_address_func) (val + insn_start, info);
         else
           fprintf_fn (stream, "%d", val);

         sep = ",";
         addr += size;
       }
     return op->len;
   }

fail:
 info->memory_error_func (status, addr, info);
 return -1;
}