/*
 *  This file is part of x48, an emulator of the HP-48sx Calculator.
 *  Copyright (C) 1994  Eddie C. Dost  (ecd@dressler.de)
 *
 *  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, Cambridge, MA 02139, USA.
 */

/* $Log: disasm.c,v $
 * Revision 1.1.1.3  1995/07/11  22:17:22  alex
 * -
 *
 * Revision 1.1.1.2  1995/06/30  21:38:13  alex
 * -
 *
 * Revision 1.1.1.1  1995/06/23  23:37:25  alex
 * -
 *
 * Revision 1.1  1995/06/17  23:26:07  alex
 * -
 *
 * Revision 1.6  1995/01/11  18:20:01  ecd
 * major update to support HP48 G/GX
 *
 * Revision 1.5  1994/12/07  20:20:50  ecd
 * some minor fixes
 *
 * Revision 1.5  1994/12/07  20:20:50  ecd
 * some minor fixes
 *
 * Revision 1.4  1994/11/28  02:00:51  ecd
 * enlarged field_tbl further
 *
 * Revision 1.3  1994/11/04  03:42:34  ecd
 * fixed bug in field_tbl
 *
 * Revision 1.2  1994/11/02  14:40:38  ecd
 * completed disassembler
 *
 * Revision 1.1  1994/10/09  20:29:47  ecd
 * Initial revision
 *
 *
 * $Id: disasm.c,v 1.1.1.3 1995/07/11 22:17:22 alex Exp $
 */

#define __FunctionProto__ 1

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "disasm.h"

#define TAB_SKIP	8

int disassembler_mode = CLASS_MNEMONICS;

char *mode_name[] =
{
  "HP",
  "class"
};

static char *hex[] =
{
  "0123456789ABCDEF",
  "0123456789abcdef",
};

static char *opcode_0_tbl[32] =
{
/*
   * HP Mnemonics
   */
  "RTNSXM", "RTN", "RTNSC", "RTNCC",
  "SETHEX", "SETDEC", "RSTK=C", "C=RSTK",
  "CLRST", "C=ST", "ST=C", "CSTEX",
  "P=P+1", "P=P-1", "(NULL)", "RTI",
/*
   * Class Mnemonics
   */
  "rtnsxm", "rtn", "rtnsc", "rtncc",
  "sethex", "setdec", "push", "pop",
  "clr.3   st", "move.3  st, c", "move.3  c, st", "exg.3   c, st",
  "inc.1   p", "dec.1   p", "(null)", "rti"
};

static char *op_str_0[16] =
{
/*
   * HP Mnemonics
   */
  "A=A%cB", "B=B%cC", "C=C%cA", "D=D%cC",
  "B=B%cA", "C=C%cB", "A=A%cC", "C=C%cD",
/*
   * Class Mnemonics
   */
  "b, a", "c, b", "a, c", "c, d",
  "a, b", "b, c", "c, a", "d, c"
};

static char *op_str_1[16] =
{
/*
   * HP Mnemonics
   */
  "DAT0=A", "DAT1=A", "A=DAT0", "A=DAT1",
  "DAT0=C", "DAT1=C", "C=DAT0", "C=DAT1",
/*
   * Class Mnemonics
   */
  "a, (d0)", "a, (d1)", "(d0), a", "(d1), a",
  "c, (d0)", "c, (d1)", "(d0), c", "(d1), c"
};

static char *in_str_80[32] =
{
/*
   * HP Mnemonics
   */
  "OUT=CS", "OUT=C", "A=IN", "C=IN",
  "UNCNFG", "CONFIG", "C=ID", "SHUTDN",
  NULL, "C+P+1", "RESET", "BUSCC",
  NULL, NULL, "SREQ?", NULL,
/*
   * Class Mnemonics
   */
  "move.s  c, out", "move.3  c, out", "move.4  in, a", "move.4  in, c",
  "uncnfg", "config", "c=id", "shutdn",
  NULL, "add.a   p+1, c", "reset", "buscc",
  NULL, NULL, "sreq?", NULL
};

static char *in_str_808[32] =
{
/*
   * HP Mnemonics
   */
  "INTON", NULL, NULL, "BUSCB",
  NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL,
  "PC=(A)", "BUSCD", "PC=(C)", "INTOFF",
/*
   * Class Mnemonics
   */
  "inton", NULL, NULL, "buscb",
  NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL,
  "jmp     (a)", "buscd", "jmp     (c)", "intoff"
};

static char *op_str_81[8] =
{
/*
   * HP Mnemonics
   */
  "A", "B", "C", "D",
/*
   * Class Mnemonics
   */
  "a", "b", "c", "d",
};

static char *in_str_81b[32] =
{
/*
   * HP Mnemonics
   */
  NULL, NULL, "PC=A", "PC=C",
  "A=PC", "C=PC", "APCEX", "CPCEX",
  NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL,
/*
   * Class Mnemonics
   */
  NULL, NULL, "jmp     a", "jmp     c",
  "move.a  pc, a", "move.a  pc, c", "exg.a   a, pc", "exg.a   c, pc",
  NULL, NULL, NULL, NULL,
  NULL, NULL, NULL, NULL,
};

static char *in_str_9[16] =
{
/*
   * HP Mnemonics
   */
  "=", "#", "=", "#",
  ">", "<", ">=", "<=",
/*
   * Class Mnemonics
   */
  "eq", "ne", "eq", "ne",
  "gt", "lt", "ge", "le"
};

static char *op_str_9[16] =
{
/*
   * HP Mnemonics
   */
  "?A%sB", "?B%sC", "?C%sA", "?D%sC",
  "?A%s0", "?B%s0", "?C%s0", "?D%s0",
/*
   * Class Mnemonics
   */
  "a, b", "b, c", "c, a", "d, c",
  "a, 0", "b, 0", "c, 0", "d, 0"
};

static char *op_str_af[32] =
{
/*
   * HP Mnemonics
   */
  "A=A%sB", "B=B%sC", "C=C%sA", "D=D%sC",
  "A=A%sA", "B=B%sB", "C=C%sC", "D=D%sD",
  "B=B%sA", "C=C%sB", "A=A%sC", "C=C%sD",
  "A=B%sA", "B=C%sB", "C=A%sC", "D=C%sD",
/*
   * Class Mnemonics
   */
  "b, a", "c, b", "a, c", "c, d",
  "a, a", "b, b", "c, c", "d, d",
  "a, b", "b, c", "c, a", "d, c",
  "b, a", "c, b", "a, c", "c, d"
};

static char hp_reg_1_af[] = "ABCDABCDBCACABAC";
static char hp_reg_2_af[] = "0000BCACABCDBCCD";

static char *field_tbl[32] =
{
/*
   * HP Mnemonics
   */
  "P", "WP", "XS", "X",
  "S", "M", "B", "W",
  "P", "WP", "XS", "X",
  "S", "M", "B", "A",
/*
   * Class Mnemonics
   */
  ".p", ".wp", ".xs", ".x",
  ".s", ".m", ".b", ".w",
  ".p", ".wp", ".xs", ".x",
  ".s", ".m", ".b", ".a",
};

static char *hst_bits[8] =
{
/*
   * HP Mnemonics
   */
  "XM", "SB", "SR", "MP",
/*
   * Class Mnemonics
   */
  "xm", "sb", "sr", "mp",
};

int
#ifdef __FunctionProto__
read_int (unsigned long * addr, int n)
#else
read_int (addr, n)
     unsigned long *addr;
     int n;
#endif
{
  int i, t;

  for (i = 0, t = 0; i < n; i++)
    t |= read_nibble ((*addr)++) << (i * 4);
  return t;
}

char *
#ifdef __FunctionProto__
append_str (char *buf, char *str)
#else
append_str (buf, str)
     char *buf;
     char *str;
#endif
{
  while ((*buf = *str++))
    buf++;
  return buf;
}

char *
#ifdef __FunctionProto__
append_tab_16 (char *buf)
#else
append_tab_16 (buf)
     char *buf;
#endif
{
  int n;
  char *p;

  n = 16 - (strlen (buf) % 16);
  p = &buf[strlen (buf)];
  while (n--)
    *p++ = ' ';
  *p = '\0';
  return p;
}

char *
#ifdef __FunctionProto__
append_tab (char *buf)
#else
append_tab (buf)
     char *buf;
#endif
{
  int n;
  char *p;

  n = TAB_SKIP - (strlen (buf) % TAB_SKIP);
  p = &buf[strlen (buf)];
  while (n--)
    *p++ = ' ';
  *p = '\0';
  return p;
}

char *
#ifdef __FunctionProto__
append_field (char *buf, unsigned int fn)
#else
append_field (buf, fn)
     char *buf;
     unsigned int fn;
#endif
{
  buf = append_str (buf, field_tbl[fn + 16 * disassembler_mode]);
  return buf;
}

char *
#ifdef __FunctionProto__
append_imm_nibble (char *buf, unsigned long * addr, int n)
#else
append_imm_nibble (buf, addr, n)
     char *buf;
     unsigned long *addr;
     int n;
#endif
{
  int i;
  char t[16];

  if (disassembler_mode == CLASS_MNEMONICS)
    {
      *buf++ = '#';
      if (n > 1)
	*buf++ = '$';
    }
  if (n > 1)
    {
      for (i = 0; i < n; i++)
	t[i] = hex[disassembler_mode][read_nibble ((*addr)++)];
      for (i = n - 1; i >= 0; i--)
	{
	  *buf++ = t[i];
	}
      *buf = '\0';
    }
  else
    {
      sprintf (t, "%d", read_nibble ((*addr)++));
      buf = append_str (buf, t);
    }
  return buf;
}

char *
#ifdef __FunctionProto__
append_addr (char *buf, unsigned long addr)
#else
append_addr (buf, addr)
     char *buf;
     unsigned long addr;
#endif
{
  int shift;
  long mask;

  if (disassembler_mode == CLASS_MNEMONICS)
    {
      *buf++ = '$';
    }
  for (mask = 0xf0000, shift = 16; mask != 0; mask >>= 4, shift -= 4)
    *buf++ = hex[disassembler_mode][(addr & mask) >> shift];
  *buf = '\0';
  return buf;
}

char *
#ifdef __FunctionProto__
append_r_addr (char *buf, unsigned long * pc, long disp, int n, int offset)
#else
append_r_addr (buf, pc, disp, n, offset)
     char *buf;
     unsigned long *pc;
     long disp;
     int n;
     int offset;
#endif
{
  long sign;

  sign = 1 << (n * 4 - 1);
  if (disp & sign)
    disp |= ~(sign - 1);
  *pc += disp;

  switch (disassembler_mode)
    {
    case HP_MNEMONICS:
      if (disp < 0)
	{
	  buf = append_str (buf, "-");
	  disp = -disp - offset;
	}
      else
	{
	  buf = append_str (buf, "+");
	  disp += offset;
	}
      buf = append_addr (buf, disp);
      break;
    case CLASS_MNEMONICS:
      if (disp < 0)
	{
	  buf = append_str (buf, "-");
	  disp = -disp - offset;
	}
      else
	{
	  buf = append_str (buf, "+");
	  disp += offset;
	}
      buf = append_addr (buf, disp);
      break;
    default:
      buf = append_str (buf, "Unknown disassembler mode");
      break;
    }
  return buf;
}

char *
#ifdef __FunctionProto__
append_pc_comment (char *buf, unsigned long pc)
#else
append_pc_comment (buf, pc)
     char *buf;
     unsigned long pc;
#endif
{
  char *p = buf;

  while (strlen (buf) < 4 * TAB_SKIP)
    p = append_tab (buf);

  switch (disassembler_mode)
    {
    case HP_MNEMONICS:
      p = append_str (p, "# Address: ");
      p = append_addr (p, pc);
      break;
    case CLASS_MNEMONICS:
      p = append_str (p, "; address: ");
      p = append_addr (p, pc);
      break;
    default:
      p = append_str (p, "Unknown disassembler mode");
      break;
    }
  return p;
}


char *
#ifdef __FunctionProto__
append_hst_bits (char *buf, int n)
#else
append_hst_bits (buf, n)
     char *buf;
     int n;
#endif
{
  int i;
  char *p = buf;

  switch (disassembler_mode)
    {
    case HP_MNEMONICS:
      for (i = 0; i < 4; i++)
	if (n & (1 << i))
	  {
	    if (p != buf)
	      p = append_str (p, "=");
	    p = append_str (p, hst_bits[i + 4 * disassembler_mode]);
	  }
      break;

    case CLASS_MNEMONICS:
      while (strlen (buf) < 4 * TAB_SKIP)
	p = append_tab (buf);
      p = &buf[strlen (buf)];
      p = append_str (p, "; hst bits: ");

      for (buf = p, i = 0; i < 4; i++)
	if (n & (1 << i))
	  {
	    if (p != buf)
	      p = append_str (p, ", ");
	    p = append_str (p, hst_bits[i + 4 * disassembler_mode]);
	  }
      break;

    default:
      p = append_str (p, "Unknown disassembler mode");
      break;
    }

  return p;
}

char *
#ifdef __FunctionProto__
disasm_1 (unsigned long * addr, char *out)
#else
disasm_1 (addr, out)
     unsigned long *addr;
     char *out;
#endif
{
  unsigned int n;
  unsigned int fn;
  char *p;
  char buf[20];
  char c;

  p = out;
  switch ((n = read_nibble ((*addr)++)))
    {
    case 0:
    case 1:
      fn = read_nibble ((*addr)++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  c = (char) ((fn < 8) ? 'A' : 'C');
          fn = (fn & 7);
          if (fn > 4)
	    fn -= 4;
	  if (n == 0)
	    sprintf (buf, "R%d=%c", fn, c);
	  else
	    sprintf (buf, "%c=R%d", c, fn);
	  p = append_str (out, buf);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, "move.w");
	  p = append_tab (out);
	  c = (char) ((fn < 8) ? 'a' : 'c');
          fn = (fn & 7);
          if (fn > 4)
	    fn -= 4;
	  if (n == 0)
	    sprintf (buf, "%c, r%d", c, fn);
	  else
	    sprintf (buf, "r%d, %c", fn, c);
	  p = append_str (p, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 2:
      fn = read_nibble ((*addr)++);
      fn = (fn & 7);
      if (fn > 4)
	fn -= 4;
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  c = (char) ((fn < 8) ? 'A' : 'C');
	  sprintf (buf, "%cR%dEX", c, fn);
	  p = append_str (out, buf);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, "exg.w");
	  p = append_tab (out);
	  c = (char) ((fn < 8) ? 'a' : 'c');
	  sprintf (buf, "%c, r%d", c, fn);
	  p = append_str (p, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 3:
      n = read_nibble ((*addr)++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  c = (n & 4) ? 'C' : 'A';
	  if (n & 2)
	    {
	      if (n < 8)
		{
		  sprintf (buf, "%cD%dEX", c, (n & 1));
		}
	      else
		{
		  sprintf (buf, "%cD%dXS", c, (n & 1));
		}
	    }
	  else
	    {
	      if (n < 8)
		{
		  sprintf (buf, "D%d=%c", (n & 1), c);
		}
	      else
		{
		  sprintf (buf, "D%d=%cS", (n & 1), c);
		}
	    }
	  p = append_str (out, buf);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (n & 2) ? "exg." : "move.");
	  p = append_str (p, (n < 8) ? "a" : "4");
	  p = append_tab (out);
	  c = (n & 4) ? 'c' : 'a';
	  sprintf (buf, "%c, d%d", c, (n & 1));
	  p = append_str (p, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 4:
    case 5:
      fn = read_nibble ((*addr)++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  p = append_str (out, op_str_1[(fn & 7) + 8 * disassembler_mode]);
	  p = append_tab (out);
	  if (n == 4)
	    {
	      p = append_str (p, (fn < 8) ? "A" : "B");
	    }
	  else
	    {
	      n = read_nibble ((*addr)++);
	      if (fn < 8)
		{
		  p = append_field (p, n);
		}
	      else
		{
		  sprintf (buf, "%d", n + 1);
		  p = append_str (p, buf);
		}
	    }
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, "move");
	  if (n == 4)
	    {
	      p = append_str (p, ".");
	      p = append_str (p, (fn < 8) ? "a" : "b");
	    }
	  else
	    {
	      n = read_nibble ((*addr)++);
	      if (fn < 8)
		{
		  p = append_field (p, n);
		}
	      else
		{
		  sprintf (buf, ".%d", n + 1);
		  p = append_str (p, buf);
		}
	    }
	  p = append_tab (out);
	  p = append_str (p, op_str_1[(fn & 7) + 8 * disassembler_mode]);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 6:
    case 7:
    case 8:
    case 0xc:
      fn = read_nibble ((*addr)++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if (n == 6 || n == 8)
	    p = append_str (out, "D0=D0");
	  else
	    p = append_str (out, "D1=D1");
	  if (n < 8)
	    p = append_str (p, "+");
	  else
	    p = append_str (p, "-");
	  p = append_tab (out);
	  sprintf (buf, "%d", fn + 1);
	  p = append_str (p, buf);
	  break;
	case CLASS_MNEMONICS:
	  if (n < 8)
	    p = append_str (out, "add.a");
	  else
	    p = append_str (out, "sub.a");
	  p = append_tab (out);
	  sprintf (buf, "#%d, ", fn + 1);
	  p = append_str (p, buf);
	  if (n == 6 || n == 8)
	    p = append_str (p, "d0");
	  else
	    p = append_str (p, "d1");
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 9:
    case 0xa:
    case 0xb:
    case 0xd:
    case 0xe:
    case 0xf:
      c = (char) ((n < 0xd) ? '0' : '1');
      switch (n & 3)
	{
	case 1:
	  n = 2;
	  break;
	case 2:
	  n = 4;
	  break;
	case 3:
	  n = 5;
	  break;
	}
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, "D%c=(%d)", c, n);
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_imm_nibble (p, addr, n);
	  break;
	case CLASS_MNEMONICS:
	  if (n == 5)
	    {
	      sprintf (buf, "move.a");
	    }
	  else if (n == 4)
	    {
	      sprintf (buf, "move.as");
	    }
	  else
	    {
	      sprintf (buf, "move.b");
	    }
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_imm_nibble (p, addr, n);
	  sprintf (buf, ", d%c", c);
	  p = append_str (p, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    default:
      break;
    }
  return p;
}


char *
#ifdef __FunctionProto__
disasm_8 (unsigned long * addr, char *out)
#else
disasm_8 (addr, out)
     unsigned long *addr;
     char *out;
#endif
{
  unsigned int n;
  unsigned int fn;
  char *p = out;
  char c;
  char buf[20];
  unsigned long disp, pc;

  fn = read_nibble ((*addr)++);
  switch (fn)
    {
    case 0:
      n = read_nibble ((*addr)++);
      if (NULL != (p = in_str_80[n + 16 * disassembler_mode]))
	{
	  p = append_str (out, p);
	  return p;
	}
      switch (n)
	{
	case 8:
	  fn = read_nibble ((*addr)++);
	  if (NULL != (p = in_str_808[fn + 16 * disassembler_mode]))
	    {
	      p = append_str (out, p);
	      return p;
	    }
	  switch (fn)
	    {
	    case 1:
	      n = read_nibble ((*addr)++);
	      if (n == 0)
		{
		  switch (disassembler_mode)
		    {
		    case HP_MNEMONICS:
		      p = append_str (out, "RSI");
		      break;
		    case CLASS_MNEMONICS:
		      p = append_str (out, "rsi");
		      break;
		    default:
		      p = append_str (out, "Unknown disassembler mode");
		      break;
		    }
		}
	      break;
	    case 2:
	      n = read_nibble ((*addr)++);
	      switch (disassembler_mode)
		{
		case HP_MNEMONICS:
		  if (n < 5)
		    {
		      sprintf (buf, "LA(%d)", n + 1);
		    }
		  else
		    {
		      sprintf (buf, "LAHEX");
		    }
		  p = append_str (out, buf);
		  p = append_tab (out);
		  p = append_imm_nibble (p, addr, n + 1);
		  break;
		case CLASS_MNEMONICS:
		  sprintf (buf, "move.%d", n + 1);
		  p = append_str (out, buf);
		  p = append_tab (out);
		  p = append_imm_nibble (p, addr, n + 1);
		  sprintf (buf, ", a.p");
		  p = append_str (p, buf);
		  break;
		default:
		  p = append_str (out, "Unknown disassembler mode");
		  break;
		}
	      break;

	    case 4:
	    case 5:
	    case 8:
	    case 9:

	      switch (disassembler_mode)
		{
		case HP_MNEMONICS:
		  sprintf (buf, "%cBIT=%d", (fn & 8) ? 'C' : 'A',
			   (fn & 1) ? 1 : 0);
		  p = append_str (out, buf);
		  p = append_tab (out);
		  p = append_imm_nibble (p, addr, 1);
		  break;
		case CLASS_MNEMONICS:
		  p = append_str (out, (fn & 1) ? "bset" : "bclr");
		  p = append_tab (out);
		  p = append_imm_nibble (p, addr, 1);
		  p = append_str (p, (fn & 8) ? ", c" : ", a");
		  break;
		default:
		  p = append_str (out, "Unknown disassembler mode");
		  break;
		}
	      break;

	    case 6:
	    case 7:
	    case 0xa:
	    case 0xb:

	      n = read_nibble ((*addr)++);
	      pc = *addr;
	      disp = read_int (addr, 2);

	      switch (disassembler_mode)
		{
		case HP_MNEMONICS:
		  c = (char) ((fn < 0xa) ? 'A' : 'C');
		  sprintf (buf, "?%cBIT=%d", c, (fn & 1) ? 1 : 0);
		  p = append_str (out, buf);
		  p = append_tab (out);
		  sprintf (buf, "%d", n);
		  p = append_str (p, buf);
		  if (disp != 0)
		    {
		      p = append_str (p, ", GOYES ");
		      p = append_r_addr (p, &pc, disp, 2, 5);
		      p = append_pc_comment (out, pc);
		    }
		  else
		    p = append_str (p, ", RTNYES");
		  break;
		case CLASS_MNEMONICS:
		  c = (char) ((fn < 0xa) ? 'a' : 'c');
		  p = append_str (out, (disp == 0) ? "rt" : "b");
		  p = append_str (p, (fn & 1) ? "bs" : "bc");
		  p = append_tab (out);
		  sprintf (buf, "#%d, %c", n, c);
		  p = append_str (p, buf);
		  if (disp != 0)
		    {
		      p = append_str (p, ", ");
		      p = append_r_addr (p, &pc, disp, 2, 5);
		      p = append_pc_comment (out, pc);
		    }
		  break;
		default:
		  p = append_str (out, "Unknown disassembler mode");
		  break;
		}
	      break;

	    default:
	      break;
	    }
	  break;

	case 0xc:
	case 0xd:
	case 0xf:
	  fn = read_nibble ((*addr)++);
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, (n == 0xf) ? "%c%cEX" : "%c=%c",
		       (n == 0xd) ? 'P' : 'C', (n == 0xd) ? 'C' : 'P');
	      p = append_str (out, buf);
	      p = append_tab (out);
	      sprintf (buf, "%d", fn);
	      p = append_str (p, buf);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, (n == 0xf) ? "exg.1" : "move.1");
	      p = append_tab (out);
	      sprintf (buf, (n == 0xd) ? "p, c.%d" : "c.%d, p", fn);
	      p = append_str (p, buf);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	default:
	  break;

	}
      break;

    case 1:
      switch (n = read_nibble ((*addr)++))
	{
	case 0:
	case 1:
	case 2:
	case 3:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, "%sSLC", op_str_81[(n & 3) + 4 * disassembler_mode]);
	      p = append_str (out, buf);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, "rol.w");
	      p = append_tab (out);
	      p = append_str (p, "#4, ");
	      p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	case 4:
	case 5:
	case 6:
	case 7:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, "%sSRC", op_str_81[(n & 3) + 4 * disassembler_mode]);
	      p = append_str (out, buf);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, "ror.w");
	      p = append_tab (out);
	      p = append_str (p, "#4, ");
	      p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	case 8:
	  fn = read_nibble ((*addr)++);
	  n = read_nibble ((*addr)++);
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, "%s=%s%cCON",
		       op_str_81[(n & 3) + 4 * disassembler_mode],
		       op_str_81[(n & 3) + 4 * disassembler_mode],
		       (n < 8) ? '+' : '-');
	      p = append_str (out, buf);
	      p = append_tab (out);
	      p = append_field (p, fn);
	      fn = read_nibble ((*addr)++);
	      sprintf (buf, ", %d", fn + 1);
	      p = append_str (p, buf);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, (n < 8) ? "add" : "sub");
	      p = append_field (p, fn);
	      p = append_tab (out);
	      fn = read_nibble ((*addr)++);
	      sprintf (buf, "#%d, ", fn + 1);
	      p = append_str (p, buf);
	      p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	case 9:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, "%sSRB.F",
		       op_str_81[(n & 3) + 4 * disassembler_mode]);
	      p = append_str (out, buf);
	      p = append_tab (out);
	      p = append_field (p, read_nibble ((*addr)++));
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, "lsr");
	      p = append_field (p, read_nibble ((*addr)++));
	      p = append_tab (out);
	      p = append_str (p, "#1, ");
	      p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	case 0xa:
	  fn = read_nibble ((*addr)++);
	  n = read_nibble ((*addr)++);
	  if (n > 2)
	    break;
	  c = (char) read_nibble ((*addr)++);
	  if (((int) c & 7) > 4)
	    break;
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      if (n == 2)
		{
		  sprintf (buf, "%cR%dEX.F", ((int) c < 8) ? 'A' : 'C',
			   (int) c & 7);
		}
	      else if (n == 1)
		{
		  sprintf (buf, "%c=R%d.F", ((int) c < 8) ? 'A' : 'C',
			   (int) c & 7);
		}
	      else
		{
		  sprintf (buf, "R%d=%c.F", (int) c & 7,
			   ((int) c < 8) ? 'A' : 'C');
		}
	      p = append_str (out, buf);
	      p = append_tab (out);
	      p = append_field (p, fn);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, (n == 2) ? "exg" : "move");
	      p = append_field (p, fn);
	      p = append_tab (out);
	      if (n == 1)
		{
		  sprintf (buf, "r%d", (int) c & 7);
		  p = append_str (p, buf);
		}
	      else
		p = append_str (p, ((int) c < 8) ? "a" : "c");
	      p = append_str (p, ", ");
	      if (n == 1)
		p = append_str (p, ((int) c < 8) ? "a" : "c");
	      else
		{
		  sprintf (buf, "r%d", (int) c & 7);
		  p = append_str (p, buf);
		}
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	case 0xb:
	  n = read_nibble ((*addr)++);
	  if ((n < 2) || (n > 7))
	    break;

	  p = append_str (out, in_str_81b[n + 16 * disassembler_mode]);
	  break;

	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      sprintf (buf, "%sSRB", op_str_81[(n & 3) + 4 * disassembler_mode]);
	      p = append_str (out, buf);
	      break;
	    case CLASS_MNEMONICS:
	      p = append_str (out, "lsr.w");
	      p = append_tab (out);
	      p = append_str (p, "#1, ");
	      p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]);
	      break;
	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      break;
	    }
	  break;

	default:
	  break;
	}
      break;

    case 2:
      n = read_nibble ((*addr)++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if (n == 0xf)
	    {
	      p = append_str (out, "CLRHST");
	    }
	  else
	    {
	      p = append_hst_bits (out, n);
	      p = append_str (p, "=0");
	    }
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, "clr.1");
	  p = append_tab (out);
	  sprintf (buf, "#%d, hst", n);
	  p = append_str (p, buf);
	  p = append_hst_bits (out, n);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 3:
      n = read_nibble ((*addr)++);
      pc = *addr;
      disp = read_int (addr, 2);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  p = append_str (out, "?");
	  p = append_hst_bits (p, n);
	  p = append_str (p, "=0");
	  p = append_tab (out);
	  if (disp != 0)
	    {
	      p = append_str (p, "GOYES ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  else
	    p = append_str (p, "RTNYES");
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (disp == 0) ? "rt" : "b");
	  p = append_str (p, "eq.1");
	  p = append_tab (out);
	  sprintf (buf, "#%d, hst", n);
	  p = append_str (p, buf);
	  if (disp != 0)
	    {
	      p = append_str (p, ", ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  p = append_hst_bits (out, n);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 4:
    case 5:
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, "ST=%d", (fn == 4) ? 0 : 1);
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_imm_nibble (p, addr, 1);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (fn == 4) ? "bclr" : "bset");
	  p = append_tab (out);
	  p = append_imm_nibble (p, addr, 1);
	  p = append_str (p, ", st");
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 6:
    case 7:
      n = read_nibble ((*addr)++);
      pc = *addr;
      disp = read_int (addr, 2);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, "?ST=%d", (fn == 6) ? 0 : 1);
	  p = append_str (out, buf);
	  p = append_tab (out);
	  sprintf (buf, "%d", n);
	  p = append_str (p, buf);
	  if (disp != 0)
	    {
	      p = append_str (p, ", GOYES ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  else
	    p = append_str (p, ", RTNYES");
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (disp == 0) ? "rt" : "b");
	  p = append_str (p, (fn == 6) ? "bc" : "bs");
	  p = append_tab (out);
	  sprintf (buf, "#%d, st", n);
	  p = append_str (p, buf);
	  if (disp != 0)
	    {
	      p = append_str (p, ", ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 8:
    case 9:
      n = read_nibble ((*addr)++);
      pc = *addr;
      disp = read_int (addr, 2);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, "?P%c", (fn == 8) ? '#' : '=');
	  p = append_str (out, buf);
	  p = append_tab (out);
	  sprintf (buf, "%d", n);
	  p = append_str (p, buf);
	  if (disp != 0)
	    {
	      p = append_str (p, ", GOYES ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  else
	    p = append_str (p, ", RTNYES");
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (disp == 0) ? "rt" : "b");
	  p = append_str (p, (fn == 8) ? "ne.1" : "eq.1");
	  p = append_tab (out);
	  sprintf (buf, "#%d, p", n);
	  p = append_str (p, buf);
	  if (disp != 0)
	    {
	      p = append_str (p, ", ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 0xc:
    case 0xe:
      pc = *addr;
      if (fn == 0xe)
	pc += 4;
      disp = read_int (addr, 4);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  p = append_str (out, (fn == 0xc) ? "GOLONG" : "GOSUBL");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6);
	  p = append_pc_comment (out, pc);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (fn == 0xc) ? "bra.4" : "bsr.4");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6);
	  p = append_pc_comment (out, pc);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 0xd:
    case 0xf:
      pc = read_int (addr, 5);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  p = append_str (out, (fn == 0xd) ? "GOVLNG" : "GOSBVL");
	  p = append_tab (out);
	  p = append_addr (p, pc);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (fn == 0xd) ? "jmp" : "jsr");
	  p = append_tab (out);
	  p = append_addr (p, pc);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    default:
      break;

    }
  return p;
}


unsigned long
#ifdef __FunctionProto__
disassemble (unsigned long addr, char *out)
#else
disassemble (addr, out)
     unsigned long addr;
     char *out;
#endif
{
  unsigned int n;
  unsigned int fn;
  char *p = out;
  char c;
  char buf[20];
  unsigned long disp, pc;

  switch (n = read_nibble (addr++))
    {
    case 0:
      if ((n = read_nibble (addr++)) != 0xe)
	{
	  p = append_str (out, opcode_0_tbl[n + 16 * disassembler_mode]);
	  break;
	}
      fn = read_nibble (addr++);
      n = read_nibble (addr++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, op_str_0[(n & 7) + 8 * HP_MNEMONICS],
		   (n < 8) ? '&' : '!');
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_field (p, fn);
	  break;
	case CLASS_MNEMONICS:
	  p = append_str (out, (n < 8) ? "and" : "or");
	  p = append_field (p, fn);
	  p = append_tab (out);
	  p = append_str (p, op_str_0[(n & 7) + 8 * CLASS_MNEMONICS]);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 1:
      p = disasm_1 (&addr, out);
      break;

    case 2:
      n = read_nibble (addr++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  sprintf (buf, "P=%d", n);
	  p = append_str (out, buf);
	  break;
	case CLASS_MNEMONICS:
	  sprintf (buf, "move.1  #%d, p", n);
	  p = append_str (out, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 3:
      fn = read_nibble (addr++);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if (fn < 5)
	    {
	      sprintf (buf, "LC(%d)", fn + 1);
	    }
	  else
	    {
	      sprintf (buf, "LCHEX");
	    }
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_imm_nibble (p, &addr, fn + 1);
	  break;
	case CLASS_MNEMONICS:
	  sprintf (buf, "move.%d", fn + 1);
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_imm_nibble (p, &addr, fn + 1);
	  sprintf (buf, ", c.p");
	  p = append_str (p, buf);
	  break;
	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 4:
    case 5:
      pc = addr;
      disp = read_int (&addr, 2);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if (disp == 2)
	    {
	      p = append_str (out, "NOP3");
	      break;
	    }
	  sprintf (buf, (disp == 0) ? "RTN%sC" : "GO%sC", (n == 4) ? "" : "N");
	  p = append_str (out, buf);
	  if (disp != 0)
	    {
	      p = append_tab (out);
	      p = append_r_addr (p, &pc, disp, 2, 1);
	      p = append_pc_comment (out, pc);
	    }
	  break;

	case CLASS_MNEMONICS:
	  if (disp == 2)
	    {
	      p = append_str (out, "nop3");
	      break;
	    }
	  p = append_str (out, (disp == 0) ? "rtc" : "bc");
	  p = append_str (p, (n == 4) ? "s" : "c");
	  if (disp != 0)
	    {
	      p = append_tab (out);
	      p = append_r_addr (p, &pc, disp, 2, 1);
	      p = append_pc_comment (out, pc);
	    }
	  break;

	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 6:
      pc = addr;
      disp = read_int (&addr, 3);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if (disp == 3)
	    {
	      p = append_str (out, "NOP4");
	      break;
	    }
	  if (disp == 4)
	    {
	      p = append_str (out, "NOP5");
	      break;
	    }
	  p = append_str (out, "GOTO");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 3, 1);
	  p = append_pc_comment (out, pc);
	  break;

	case CLASS_MNEMONICS:
	  if (disp == 3)
	    {
	      p = append_str (out, "nop4");
	      break;
	    }
	  if (disp == 4)
	    {
	      p = append_str (out, "nop5");
	      addr++;
	      break;
	    }
	  p = append_str (out, "bra.3");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 3, 1);
	  p = append_pc_comment (out, pc);
	  break;

	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 7:
      pc = addr + 3;
      disp = read_int (&addr, 3);
      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  p = append_str (out, "GOSUB");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 3, 4);
	  p = append_pc_comment (out, pc);
	  break;

	case CLASS_MNEMONICS:
	  p = append_str (out, "bsr.3");
	  p = append_tab (out);
	  p = append_r_addr (p, &pc, disp, 3, 4);
	  p = append_pc_comment (out, pc);
	  break;

	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    case 8:
      fn = read_nibble (addr);	/* PEEK */
      if (fn != 0xa && fn != 0xb)
	{
	  p = disasm_8 (&addr, out);
	  break;
	}
      /* Fall through */

    case 9:
      fn = read_nibble (addr++);
      if (n == 8)
	{
	  c = (char) ((fn == 0xa) ? 0 : 1);
	  fn = 0xf;
	}
      else
	{
	  c = (char) ((fn < 8) ? 0 : 1);
	  fn &= 7;
	}

      n = read_nibble (addr++);
      pc = addr;
      disp = read_int (&addr, 2);

      switch (disassembler_mode)
	{
	case HP_MNEMONICS:
	  if ((c == 0) && (n >= 8))
	    sprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS + 4],
		     in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]);
	  else
	    sprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS],
		     in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]);
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_field (p, fn);
	  p = append_str (p, ", ");
	  p = append_str (p, (disp == 0) ? "RTNYES" : "GOYES ");
	  if (disp != 0)
	    {
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  break;

	case CLASS_MNEMONICS:
	  p = append_str (out, (disp == 0) ? "rt" : "b");
	  p = append_str (p, in_str_9[((n >> 2) & 3) + 4 * c + 8 * CLASS_MNEMONICS]);
	  p = append_field (p, fn);
	  p = append_tab (out);
	  if ((c == 0) && (n >= 8))
	    p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS + 4]);
	  else
	    p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS]);
	  if (disp != 0)
	    {
	      p = append_str (p, ", ");
	      p = append_r_addr (p, &pc, disp, 2, 3);
	      p = append_pc_comment (out, pc);
	    }
	  break;

	default:
	  p = append_str (out, "Unknown disassembler mode");
	  break;
	}
      break;

    default:
      switch (n)
	{
	case 0xa:
	  fn = read_nibble (addr++);
	  c = (char) ((fn < 8) ? 0 : 1);
	  fn &= 7;
	  disp = 0xa;
	  break;
	case 0xb:
	  fn = read_nibble (addr++);
	  c = (char) ((fn < 8) ? 0 : 1);
	  fn &= 7;
	  disp = 0xb;
	  break;
	case 0xc:
	case 0xd:
	  fn = 0xf;
	  c = (char) (n & 1);
	  disp = 0xa;
	  break;
	case 0xe:
	case 0xf:
	  fn = 0xf;
	  c = (char) (n & 1);
	  disp = 0xb;
	  break;
	default:
	  fn = 0;
	  disp = 0;
	  c = 0;
	  break;
	}

      n = read_nibble (addr++);
      pc = 0;

      switch (disp)
	{
	case 0xa:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      if (c == 0)
		{
		  if (n < 0xc)
		    {
		      p = "+";
		    }
		  else
		    {
		      p = "%c=%c-1";
		      pc = 2;
		    }
		}
	      else
		{
		  if (n < 4)
		    {
		      p = "%c=0";
		      pc = 1;
		    }
		  else if (n >= 0xc)
		    {
		      p = "%c%cEX";
		      pc = 3;
		    }
		  else
		    {
		      p = "%c=%c";
		      pc = 3;
		    }
		}
	      break;

	    case CLASS_MNEMONICS:
	      if (c == 0)
		{
		  if (n < 0xc)
		    {
		      p = "add";
		    }
		  else
		    {
		      p = "dec";
		      pc = 1;
		    }
		}
	      else
		{
		  if (n < 4)
		    {
		      p = "clr";
		      pc = 1;
		    }
		  else if (n >= 0xc)
		    {
		      p = "exg";
		    }
		  else
		    {
		      p = "move";
		      if (n < 8)
			n -= 4;
		    }
		}
	      break;

	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      return addr;
	    }
	  break;

	case 0xb:
	  switch (disassembler_mode)
	    {
	    case HP_MNEMONICS:
	      if (c == 0)
		{
		  if (n >= 0xc)
		    {
		      p = "-";
		    }
		  else if ((n >= 4) && (n <= 7))
		    {
		      p = "%c=%c+1";
		      pc = 2;
		      n -= 4;
		    }
		  else
		    {
		      p = "-";
		    }
		}
	      else
		{
		  if (n < 4)
		    {
		      p = "%cSL";
		      pc = 1;
		    }
		  else if (n < 8)
		    {
		      p = "%cSR";
		      pc = 1;
		    }
		  else if (n < 0xc)
		    {
		      p = "%c=%c-1";
		      pc = 2;
		    }
		  else
		    {
		      p = "%c=-%c-1";
		      pc = 2;
		    }
		}
	      break;

	    case CLASS_MNEMONICS:
	      if (c == 0)
		{
		  if (n >= 0xc)
		    {
		      p = "subr";
		    }
		  else if ((n >= 4) && (n <= 7))
		    {
		      p = "inc";
		      pc = 1;
		      n -= 4;
		    }
		  else
		    {
		      p = "sub";
		    }
		}
	      else
		{
		  pc = 1;
		  if (n < 4)
		    {
		      p = "lsl";
		    }
		  else if (n < 8)
		    {
		      p = "lsr";
		    }
		  else if (n < 0xc)
		    {
		      p = "neg";
		    }
		  else
		    {
		      p = "not";
		    }
		}
	      break;

	    default:
	      p = append_str (out, "Unknown disassembler mode");
	      return addr;
	    }
	  break;

	}

      switch (disassembler_mode)
	{
	case HP_MNEMONICS:

	  if (pc == 0)
	    {
	      sprintf (buf, op_str_af[n + 16 * HP_MNEMONICS], p);
	    }
	  else if (pc == 1)
	    {
	      sprintf (buf, p, (n & 3) + 'A');
	    }
	  else if (pc == 2)
	    {
	      sprintf (buf, p, (n & 3) + 'A', (n & 3) + 'A');
	    }
	  else
	    {
	      sprintf (buf, p, hp_reg_1_af[n], hp_reg_2_af[n]);
	    }
	  p = append_str (out, buf);
	  p = append_tab (out);
	  p = append_field (p, fn);
	  break;

	case CLASS_MNEMONICS:

	  p = append_str (out, p);
	  p = append_field (p, fn);
	  p = append_tab (out);
	  if (pc == 1)
	    {
	      sprintf (buf, "%c", (n & 3) + 'a');
	      p = append_str (p, buf);
	    }
	  else
	    {
	      p = append_str (p, op_str_af[n + 16 * CLASS_MNEMONICS]);
	    }
	  break;

	default:
	  p = append_str (p, "Unknown disassembler mode");
	  break;
	}

      break;
    }
  *p = '\0';
  return addr;
}


syntax highlighted by Code2HTML, v. 0.9.1