/* code.c -- STAR Code Generation

This file is part of STAR, the Saturn Macro Assembler.

   STAR is not distributed by the Free Software Foundation. Do not ask
them for a copy or how to obtain new releases. Instead, send e-mail to
the address below. STAR is merely covered by the GNU General Public
License.

Please send your comments, ideas, and bug reports to
Jan Brittenson <bson@ai.mit.edu>

*/


/* Copyright (C) 1990 Jan Brittenson.

   STAR 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 1, or (at your option) any
later version.

   STAR 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 STAR; see the file COPYING. If not, to obtain a copy, write
to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
USA, or send e-mail to bson@ai.mit.edu. */

#include <stdio.h>
#include "star.h"
#include "code.h"

#define	GEN1	&arg[0]
#define	GEN2	&arg[1]
#define GEN3	&arg[2]
#define GEN4	&arg[3]
  
  
/* Global data */
extern struct istruct
  instbl[];			/* Instruction table */

extern struct astruct
  arg[];			/* Argument list */

extern
  pass, errcnt, lincnt;


/* Local data */
static struct astruct
  *argptr;

static
  argcnt,
  dummy;			/* Dummy variable */


/* Argument type patters */
static
  sd_1_table[] = {
    AP_REG|R_B, AP_REG|R_A,	/* B A */
    AP_REG|R_C, AP_REG|R_B,	/* C B */
    AP_REG|R_A, AP_REG|R_C,	/* A C */
    AP_REG|R_C, AP_REG|R_D,	/* C D */
    AP_REG|R_A, AP_REG|R_B,	/* A B */
    AP_REG|R_B, AP_REG|R_C,	/* B C */
    AP_REG|R_C, AP_REG|R_A,	/* C A */
    AP_REG|R_D, AP_REG|R_C},	/* D C */

  s_1_table[] = {
    AP_REG|R_A,			/* A */
    AP_REG|R_B,			/* B */
    AP_REG|R_C,			/* C */
    AP_REG|R_D},		/* D */

  sd_2_table[] = {
    AP_REG|R_A, AP_REG|R_B,	/* A B */
    AP_REG|R_B, AP_REG|R_C,	/* B C */
    AP_REG|R_C, AP_REG|R_A,	/* C A */
    AP_REG|R_D, AP_REG|R_C},	/* D C */

  sacd01_table[] = {
    AP_REG|R_A, AP_ID01,	/* A @Dn */
    AP_REG|R_A, AP_ID01,	/* A @Dn */
    AP_ID01, AP_REG|R_A,	/* @Dn A */
    AP_ID01, AP_REG|R_A,	/* @Dn A */
    AP_REG|R_C, AP_ID01,	/* C @Dn */
    AP_REG|R_C, AP_ID01,	/* C @Dn */
    AP_ID01, AP_REG|R_C,	/* @Dn C */
    AP_ID01, AP_REG|R_C},	/* @Dn C */

  add_sd_table[] = {
    AP_REG|R_B, AP_REG|R_A,	/* B A */
    AP_REG|R_C, AP_REG|R_B,	/* C B */
    AP_REG|R_A, AP_REG|R_C,	/* A C */
    AP_REG|R_C, AP_REG|R_D,	/* C D */
    AP_REG|R_A, AP_REG|R_A,	/* A A */
    AP_REG|R_B, AP_REG|R_B,	/* B B */
    AP_REG|R_C, AP_REG|R_C,	/* C C */
    AP_REG|R_D, AP_REG|R_D,	/* D D */
    AP_REG|R_A, AP_REG|R_B,	/* A B */
    AP_REG|R_B, AP_REG|R_C,	/* B C */
    AP_REG|R_C, AP_REG|R_A,	/* C A */
    AP_REG|R_D, AP_REG|R_C};	/* D C */


/* Absolute value */
#ifndef __GNUC__
long abs_l(l) long l; { return(l >= 0 ? l : -l); }
#endif


/* Signal "Inconsistent arguments" error */
static void error_incons_args(ip)
  struct istruct *ip;
{
  if(ip)
    sgnerr("Inconsistent arguments for %s", ip->name);
  else
    sgnerr("Inconsistent arguments");
}


/* Signal "Inconsistent argument" error */
static void error_1incons_arg(ip)
  struct istruct *ip;
{
  if(ip)
    sgnerr("Inconsistent argument for %s", ip->name);
  else
    sgnerr("Inconsistent argument");
}


/* Signal "Inconsistent instruction suffix" error */
static void error_incons_suffix(expected)
  char *expected;
{
  if(expected)
    sgnerr("Inconsistent instruction suffix, expected %s", expected);
  else
    sgnerr("Inconsistent instruction suffix");
}


/* Generate code for fixed field */
static void codefield(f, c1, d1, c2, d2)
  int f, (*c1)(), d1, (*c2)(), d2;
{
  if(f == F_A)
    (*c2)(d2);
  else
    (*c1)(d1 + (c1 == code2 ? (f << 4) : f));
}


/* op */
void byte1(ip, cp)
  struct istruct *ip;
  char *cp;
{
  dcdfield(&cp, &dummy, 0);
  dcdarg(cp, 0);
  code2(ip->opcode);
}


/* op */
void word3(ip, cp)
  struct istruct *ip;
  char *cp;
{
  dcdfield(&cp, &dummy, 0);
  dcdarg(cp, 0);
  code3(ip->opcode);
}


/* op */
void word4(ip, cp)
  struct istruct *ip;
  char *cp;
{
  dcdfield(&cp, &dummy, 0);
  dcdarg(cp, 0);
  code4(ip->opcode);
}


/* op */
void word5(ip, cp)
  struct istruct *ip;
  char *cp;
{
  dcdfield(&cp, &dummy, 0);
  dcdarg(cp, 0);
  code5(ip->opcode);
}


/* op.A reg.length */
void opc1(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f;

  dcdfield(&cp, &f, FT_FIX | FT_DEFLT | F_A);

  if(f != F_A)
    error_incons_suffix(".A");

  dcdarg(cp, 1);

  if(!type(GEN1, AP_REG|ip->length))
    error_incons_args(ip);

  code2(ip->opcode);
}


/* Jumpify branch */
void jumpify(opsize, opcode, broffs, dest)
  int opsize, opcode, broffs;
  unsigned long dest;
{
  int len, jumplen;
  extern long
    loc0, loc;
  extern
    pass;


  /* First compute length of relative jump to be used */
  if(dest > (unsigned long) 0xfffff)
    {
      sgnerr("Destination x'%lx not within Saturn address space",
	     (long) dest);
      
      dest &= (unsigned long) 0xfffff;
    }
  
  len = 4 - (pass != 1 &&
	     (ABSL(dest - (loc0 + opsize + 2)) < (unsigned long) 0x7f4));

  jumplen = (len == 3 ? 4 : 6);
  
  /* Then we create the branch around */
  coden(opsize, opcode);
  code2(opsize-broffs+2+jumplen);

  /* Finally we add the jump instruction */
  if(len == 3)
    code1(6);
  else
    code2(0xc8);

  coden(len, dest-loc0-(len-2)-(opsize+2));
}


/* Jumpify branch, but stick in extra one-nibble opcode */
void jumpify_twoop(opsize, opcode, xop, broffs, dest)
  int opsize, opcode, xop, broffs;
  unsigned long dest;
{
  int len, jumplen;
  extern long
    loc0, loc;
  extern
    pass;


  /* First compute length of relative jump to be used */
  if(dest > (unsigned long) 0xfffff)
    {
      sgnerr("Destination x'%lx not within Saturn address space",
	     (long) dest);
      
      dest &= (unsigned long) 0xfffff;
    }
  
  len = 4 - (pass != 1 &&
	     (ABSL(dest - (loc0 + opsize + 1 + 2)) < (unsigned long) 0x7f4));

  jumplen = (len == 3 ? 4 : 6);
  
  /* Then we create the branch around */
  coden(opsize, opcode);
  code1(xop);
  code2(opsize+1-broffs+2+jumplen);

  /* Finally we add the jump instruction */
  if(len == 3)
    code1(6);
  else
    code2(0xc8);

  coden(len, dest-loc0-(len-2)-(opsize+1+2));
}


/* Jumpify multi-nibble suffix-computed opcode branch */
static void jumpify_field(f, l1, op1, l2, op2, l3, op3, broffs, dest)
  int f, l1, op1, l2, op2, l3, op3, broffs;
  unsigned long dest;
{
  int
    len, jumplen, firstlen, opcode, opsize;
  extern long
    loc0, loc;
  extern
    pass;


  /* First compute length of relative jump to be used */
  if(dest > (unsigned long) 0xfffff)
    {
      sgnerr("Destination x'%lx not within Saturn address space",
	     (long) dest);
      
      dest &= (unsigned long) 0xfffff;
    }
  
  if(f == F_A)
    {
      firstlen = l2;
      opcode = op2;
    }
  else
    {
      firstlen = l1;
      opcode = op1 + (l1 == 2 ? (f << 4) : f);
    }

  /* len = length of jump offset, jumplen = length of jump instruction */
  len = 4 - (pass != 1 &&
	     (ABSL(dest - (loc0 + firstlen + l3 + 2)) <
	      (unsigned long) 0x7f4));

  jumplen = (len == 3 ? 4 : 6);

  /* opsize = length of BR opcode */
  opsize = firstlen + l3;

  /* Then we create the branch around the jump */
  coden(firstlen, opcode);
  coden(l3, op3);
  code2(opsize-broffs+2+jumplen);


  /* Finally we add the jump instruction */
  if(len == 3)
    code1(6);
  else
    code2(0xc8);

  coden(len, dest-loc0-(len-2)-(opsize+2));
}

  
/* op.[34A] addr */
void frel(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, isn = FALSE;
  extern long
    static_begin, static_end,
    floating_begin, floating_end;
  extern pass;

  dcdfield(&cp, &f, FT_FIX | FT_N | FT_DEFLT | F_DEFLT);
  
  dcdarg(cp, 1);

  if(f & F_N)
    isn = TRUE;

  if(isn)
    {
      f &= 077;
      if(f != 3 && f != 4)
	{
	  error_incons_suffix(".3, .4, or .A");
	  f = 3;
	}
    }
  else
    if(!(f & (F_A | F_DEFLT)))
      {
	error_incons_suffix(".3, .4, or .A");
	f = F_A;
      }
  
  /* {jump|call}.[34A] #imm */
  if(type(GEN1, AP_TYPE|T_IMM))
     {
       if((unsigned INT) arg[0].disp1 > (unsigned INT) 0xfffff)
	 {
	   sgnerr("Destination x'%lx not within Saturn address space",
		  (long) arg[0].disp1);

	   arg[0].disp1 &= 0xfffff;
	 }

       /* If no suffix, pick one */
       if(f == F_DEFLT)

	 if(arg[0].disp1 >= (INT) static_begin &&
	    arg[0].disp1 <= (INT) static_end)
	   f = F_A;
	 else
	   if(arg[0].disp1 >= (INT) floating_begin &&
	      arg[0].disp1 <= (INT) floating_end)
	     {
	       isn = TRUE;
	       
	       if(pass != 1 && ABSL(arg[0].disp1-(INT) loc0) < 0x7f4)
		 f = 3;
	       else
		 f = 4;
	     }
	   else
	     {
	       sgnerr("Indeterminable suffix for destination x'%lx",
		      (long) arg[0].disp1);
	       
	       f = F_A;
	     }

       if(ip->opcode)
	 {
	   /* Jump */
	   
	   if(isn)
	     {
	       /* jump.n */
	       arg[0].disp1 -= (f == 3 ? 1 : 2) + loc0;
	       if(f == 3)
		 code1(6);
	       else
		 code2(0xc8);
	     }
	   else
	     {
	       /* jump.a */
	       code2(0xd8);
	       code5(arg[0].disp1); /* fix -bson */
	       return;
	     }
	 }
       else
	 {
	   /* Call */
	   
	   if(isn)
	     {
	       /* call.n */
	       arg[0].disp1 -= (f == 3 ? 4 : 6) + loc0;
	       if(f == 3)
		 code1(7);
	       else
		 code2(0xe8);
	     }
	   else
	     {
	       /* call.a */
	       code2(0xf8);
	       code5(arg[0].disp1); /* fix -bson */
	       return;
	     }
	 }
       
       /* {jump|call}.[34] drop down here */
       if(f == 3)
	 code3((unsigned) arg[0].disp1);
       else
	 code4((unsigned) arg[0].disp1);

       return;
     }
   
  /* {jump|call}.A [AC]? */
  if(type(GEN1, AP_RAC))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");
      
      code3(0xb18);
      code1(2+(arg[0].reg == R_C));
      return;
    }
  
  /* {jump|call}.A @[AC]? */
  if(type(GEN1, AP_TYPE|T_INDAC))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");
      
      code3(0x808);
      code1(0xc + (arg[0].reg == R_C ? 2 : 0));
      return;
    }
  
  error_1incons_arg(ip);
}


/* SLB.f d (ghost) */
void fslb(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f;

  dcdfield(&cp, &f, FT_FIX|FT_DEFLT|F_W);

  dcdarg(cp, 1);

  if(!type(GEN1, AP_RABCD))
    {
      sgnerr("Inconsistent argument, expected A, B, C, or D");
      arg[0].reg = R_A;
    }

  codefield(f, code2, 0x0a, code1, 0xc);

  code1(4 + arg[0].reg - R_A);
}

      
/* ADD.f s,d */
void fadd(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, sd;
  char *tmp = cp;


  dcdfield(&cp, &f, FT_FIX|FT_DEFLT|F_DEFLT);
  dcdarg(cp, 2);


  /* ADD #imm,D[01]? */
  if(types2(AP_TYPE|T_IMM, AP_D01))
    {
      if(f != F_DEFLT && f != F_A)
	error_incons_suffix(".A");

      if(arg[1].reg == R_D0)
	code2(0x61);
      else
	code2(0x71);

      chkimm(GEN1, 1L, 16L);

      code1(arg[0].disp1 - 1);
      return;
    }
  

  /* ADD.A P+1,C ? */
  if(types2(AP_TYPE|T_PPLUS1, AP_REG|R_C))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");

      code3(0x908);
      return;
    }

  /* The rest require explicit field suffixes */
  if(f == F_DEFLT)
    {
      sgnerr("Suffix is missing");

      f = F_A;
    }


  /* ADD.f #imm,d ? */
  if(types2(AP_TYPE|T_IMM, AP_RABCD))
    {
      chkimm(GEN1, 1, 16);

      code3(0x818);
      codefield(f, code1, 0, code1, 0xf);
      code1(arg[1].reg - R_A);
      code1(arg[0].disp1 - 1);
      return;
    }

  /* ADD.f s,d */
  if((sd = types_table(add_sd_table, 2, 12)) >= 0)
    {
      codefield(f, code2, 0x0a, code1, 0xc);
      code1(sd);
      return;
    }

  error_1incons_arg(NULL);
}


/* {AND|OR}.f s,d */
void opsd1(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, sd;

  dcdfield(&cp, &f, FT_FIX);
  dcdarg(cp,2);

  if((sd = types_table(sd_1_table, 2, 8)) < 0)
    {
      error_incons_args(ip);
      return;
    }

  code2(0xe0);
  codefield(f, code1, 0, code1, 0xf);
  code1(sd + ip->opcode);
}


/* {SUB|SUBN}.f s,d */
void fsub(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, sd, op = 0xc;

  dcdfield(&cp, &f, FT_FIX|FT_DEFLT|F_DEFLT);
  dcdarg(cp, 2);

  if(!ip->opcode)
    {
      /* SUB.A #imm,D[01] */
      if(types2(AP_TYPE|T_IMM, AP_D01))
	{
	  if(f != F_DEFLT && f != F_A)
	    error_incons_suffix(".A");
	      
	  chkimm(GEN1, 1L, 16L);

	  if(arg[1].reg == R_D0)
	    code2(0x81);
	  else
	    code2(0xc1);

	  code1(arg[0].disp1-1);
	  return;
	}

      if(f == F_DEFLT)
	{
	  sgnerr("Missing instruction suffix");
	  f = F_A;
	}

      /* SUB.f #imm, d */
      if(types2(AP_TYPE|T_IMM, AP_RABCD))
	{
	  chkimm(GEN1, 1L, 16L);

	  code3(0x818);
	  codefield(f, code1, 0, code1, 0xf);
	  code1(8 + arg[1].reg - R_A);
	  code1(arg[0].disp1 - 1);
	  return;
	}

      op = 0;
    }

  /* {SUB,SUBN}.f s,d */
  codefield(f, code2, 0x0b, code1, 0xe);

  if((sd = types_table(sd_1_table, 2, 8)) < 0)
    {
      error_incons_args(ip);
      sd = 0;
    }

  if(!op && sd >= 4)
    sd += 4;

  code1(sd + op);
}


/* {{RET|BR}B{S|C} #bit,reg,#dest */
void fbrb(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int s, op;
  extern opt_jumpify;


  dcdfield(&cp, &dummy, FT_NONE);

  if(ip->length)
    dcdarg(cp, 2);		/* RET */
  else
    dcdarg(cp, 3);		/* BR */

  chkimm(GEN1, 0L, 15L);

  /* #bit, HST? */
  if(type(GEN2, AP_TYPE|T_HST))
    {
      if(ip->opcode)
	error_incons_args(ip);

      code2(0x38);
      code1(arg[0].disp1);

      if(ip->length)
	code2(0);
      else
	{
	  arg[2].disp1 -= loc0 + 3;
	  
	  chkimm(GEN3, -0x80, 0xff);
	  
	  code2(arg[2].disp1);
	}


      return;
    }

  /* #imm, ST? */
  if(type(GEN2, AP_REG|R_ST))
    {
      /* Jumpify if applicable */
      if(opt_jumpify && !ip->length)
	{
	  long offs = arg[2].disp1 - loc0 - 3;
	  
	  if(offs < (long) -0x80 || offs > (long) 0x7f)
	    {
	      jumpify_twoop(2, (ip->opcode ? 0x68 : 0x78),
			    (int) arg[0].disp1,
			    3, arg[2].disp1);
	      return;
	    }
	}
      
      if(ip->opcode)
	code2(0x78);
      else
	code2(0x68);

      code1(arg[0].disp1);

      if(ip->length)
	code2(0);
      else
	{
	  arg[2].disp1 -= loc0 + 3;
	  
	  chkimm(GEN3, -0x80, 0xff);
	  
	  code2(arg[2].disp1);
	}

      return;
    }

  /* We now have #imm, [AC] */
  if(type(GEN2, AP_REG|R_A))
    op = 6;
  else
    if(type(GEN2, AP_REG|R_C))
      op = 0xa;
    else
      {
	error_1incons_arg(ip);
	op = 6;
      }

  chkimm(GEN1, 0L, 15L);

  if(opt_jumpify && !ip->length)
    {
      long offs = arg[2].disp1 - loc0 - 5;

      if(offs < (long) -0x80 || offs > (long) 0x7f)
	{
	  jumpify_twoop(4, (((op+ip->opcode) ^ 1) << 12) | 0x808,
			(int) arg[0].disp1,
			5, arg[2].disp1);
	  return;
	}
    }
  
  code3(0x808);
  code1(op + ip->opcode);

  code1(arg[0].disp1);

  if(ip->length)
    code2(0);
  else
    {
      arg[2].disp1 -= loc0 + 5;

      chkimm(GEN3, -0x80, 0xff);

      code2(arg[2].disp1);
    }
}


/* {BR|RET}C{CS} #dest */
void fbrc(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int op;
  long dest;
  extern opt_jumpify;

  dcdfield(&cp, &dummy, FT_NONE);

  if(ip->length)
    dcdarg(cp, 0);
  else
    {
      dcdarg(cp, 1);
      isimm(GEN1);

      dest = arg[0].disp1;

      if(opt_jumpify)
	{
	  long offs = dest - loc0 - 1;

	  if(offs < (long) -0x80 || offs > (long) 0x7f)
	    {
	      /* Jumpify */
	      jumpify(1, 4 + ip->opcode, 1, dest);
	      return;
	    }
	}
      else
	chkimm(GEN1, (long) -0x80, (long) 0x7f);
    }

  code1(5 - ip->opcode);

  if(ip->length)
    code2(0);
  else
    code2(dest-loc0-1);
}
  

/* {BR|RET}{EQ|NE|Z|NZ|GT|LT|GE|LE}.f s[,d],dest */
void fbrt(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int sd, op, f, isznz, isret;
  long offs;
  struct astruct *disp;
  extern opt_jumpify;


  dcdfield(&cp, &f, FT_FIX | FT_DEFLT|F_P);

  /* {BR|RET}{NZ|Z} ? */
  isznz = !(ip->length & 1) && (ip->opcode == 8 || ip->opcode == 0xc);
  isret = (ip->length >= 2);

  if(isznz)
    if(isret)
      dcdarg(cp, 1);
    else
      dcdarg(cp, 2);
  else
    if(isret)
      dcdarg(cp, 2);
    else
      dcdarg(cp, 3);

  /* {BR|RET}xx.1 P,#x[,#dest] */
  if(types2(AP_REG|R_P, AP_TYPE|T_IMM))
    {
      if((ip->length & 1) || !(!ip->opcode || ip->opcode == 4))
	{
	  error_incons_args(ip);
	  return;
	}

      if(ip->length < 2)
	isimm(GEN3);

      chkimm(GEN2, 0L, 15L);

      /* Jumpify if applicable */
      if(opt_jumpify && ip->length < 2)
	{
	  long offs = arg[2].disp1 - loc0 - 3;

	  if(offs < (long) -0x80 || offs > (long) 0x7f)
	    {
	      jumpify_twoop(2, (ip->opcode ? 0x98 : 0x88),
			    (int) arg[1].disp1,
			    3, arg[2].disp1);
	      return;
	    }
	}

      if(ip->opcode)
	code2(0x88);
      else
	code2(0x98);

      code1(arg[1].disp1);

      if(ip->length < 2)
	{
	  /* BR */
	  arg[2].disp1 -= loc0 + 3;

	  chkimm(GEN3, (long) -0x80, (long) 0x7f);

	  code2(arg[2].disp1);
	}
      else
	/* RET */
	code2(0);

      return;
    }

  /* If Z|NZ, check one arg, else two */
  if(isznz)
    {
      if((sd = types_table(s_1_table, 1, 4)) < 0)
	{
	  error_1incons_arg(ip);
	  sd = 0;
	}

      if(!isret)
	{
	  isimm(GEN2);
	  disp = GEN2;
	}
    }
  else
    {
      if((sd = types_table(sd_2_table, 2, 4)) < 0)
	{
	  error_incons_args(ip);
	  sd = 0;
	}

      if(!isret)
	{
	  isimm(GEN3);
	  disp = GEN3;
	}
    }

  /* Jumpify if applicable */
  offs = disp->disp1 - loc0 - 3;

  if(opt_jumpify && !isret)
    {
      if(offs < (long) -0x80 || offs > (long) 0x7f)
	{
	  if(ip->length & 1)
	    jumpify_field(f, 2, 0x89, 2, 0xb8,
			  1, (sd + ip->opcode) ^ (4|8),
			  3, disp->disp1);
	  else
	    jumpify_field(f, 2, 0x09, 2, 0xa8,
			  1, (sd + ip->opcode) ^ 4,
			  3, disp->disp1);
	  return;
	}
    }

  if(ip->length & 1)

    /* GT|LT|GE|LE */
    codefield(f, code2, 0x89, code2, 0xb8);
  else

    /* EQ|NE|Z|NZ */
    codefield(f, code2, 0x09, code2, 0xa8);

  code1(sd + ip->opcode);

  if(isret)

    /* RET */
    code2(0);
  else

    /* BR */
    {
      disp->disp1 -= loc0 + 3;

      chkimm(disp, (long) -0x80, (long) 0x7f);

      code2(disp->disp1);
    }
}


/* {DEC|CLR|INC|NEG|NOT}.f d */
void opd1(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, d, op1, op2;

  dcdfield(&cp, &f, FT_FIX | FT_N | FT_DEFLT|F_DEFLT);

  dcdarg(cp, 1);

  /* op.f P ? */
  if(type(GEN1, AP_REG|R_P))
    {
      /* Assert op.1 */
      if(f != (F_N|1) && f != F_DEFLT)
	error_incons_suffix(".1");

      /* Do we have a CLR? */
      if(!ip->length && ip->opcode == 1)
	{
	  /* MOVE.1 0,P */
	  code2(0x02);
	  return;
	}

      /* Assert {DEC|INC} */
      if(!((!ip->opcode && ip->length == 0xc) ||
	   (ip->opcode == 2 && ip->length == 4)))
	{
	  error_1incons_arg(ip);
	  return;
	}

      if(ip->opcode)
	code2(0xc0);
      else
	code2(0xd0);

      return;
    }
	 
  /* op.f ST? */
  if(type(GEN1, AP_REG|R_ST))
    {
      /* Assert CLR */
      if(ip->length || ip->opcode != 1)
	{
	  error_1incons_arg(ip);
	  return;
	}

      /* Assert .X */
      if(f != F_X && f != F_DEFLT)
	error_incons_suffix(".X");
	  
      code2(0x80);
      return;
    }

  /* {INC|DEC}[.A] Dn? */
  if(type(GEN1, AP_D01) &&
     ((!ip->opcode && ip->length == 0xc) ||
      (ip->opcode == 2 && ip->length == 4)))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");

      code1(1);

      if(ip->opcode)
	code1(6 + arg[0].reg - R_D0);
      else
	code1(8 + ((arg[0].reg - R_D0) << 2));

      code1(0);
      return;
    }

  /* CLR[.f] Dn? */
  if(type(GEN1, AP_D01) && (!ip->length && ip->opcode == 1))
    {
      if(f == F_DEFLT)
	f = 5;
      else
	if((f &= 077) != 2 && f != 4 && f != 5)
	  {
	    error_incons_suffix(".2, .4, or .5");
	    f = 5;
	  }

      code1(1);
      code1(9 + (arg[0].reg - R_D0) * 4 +
	    (f == 2 ? 0 : (f == 4 ? 1 : 2)));
      coden(f, 0);
      return;
    }

  /* op.f [ABCD]? */
  if((d = types_table(s_1_table, 1, 4)) < 0)
    {
      error_1incons_arg(ip);
      d = 0;
    }

  switch(ip->opcode)
    {
    case 0: op1 = 0x0a; op2 = 0xc; break;
    case 1: op1 = 0x8a; op2 = 0xd; break;
    case 2: op1 = 0x0b; op2 = 0xe; break;
    case 3: op1 = 0x8b; op2 = 0xf; break;
    default:
      fatal("Invalid opcode %d dispatched to OPD1", ip->opcode);
    }

  if(f == F_DEFLT)
    {
      sgnerr("Missing instruction suffix");
      f = F_A;
    }

  codefield(f, code2, op1, code1, op2);
  code1(ip->length + d);
}


/* {CLR|SET}B #imm,d */
void fbits(ip, cp)
  struct istruct *ip;
  char *cp;
{
  dcdfield(&cp, &dummy, FT_NONE);
  dcdarg(cp, 2);

  chkimm(GEN1, 0L, 15L);

  /* #imm, HST? */
  if(types2(AP_TYPE|T_IMM, AP_TYPE|T_HST))
    {
      /* Assert CLRB */
      if(ip->opcode)
	error_incons_args(ip);

      code2(0x28);
      code1(arg[0].disp1);
      return;
    }

  /* #imm, ST? */
  if(types2(AP_TYPE|T_IMM, AP_REG|R_ST))
    {
      code2(ip->opcode ? 0x58 : 0x48);
      code1(arg[0].disp1);
      return;
    }

  /* #imm, [AC] */
  if(type(GEN2, AP_RAC))
    {
      code3(0x808);
      code1((arg[1].reg == R_A ? 4 : 8) + ip->opcode);
      code1(arg[0].disp1);
      return;
    }

  error_incons_args(ip);
}


/* {RLN|RRN}.W d */
void frotw(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, d;

  dcdfield(&cp, &f, FT_FIX|FT_DEFLT|F_W);

  if(f != F_W)
    error_incons_suffix(".W");

  dcdarg(cp, 1);

  if((d = types_table(s_1_table, 1, 4)) < 0)
    {
      error_1incons_arg(ip);
      d = 0;
    }

  code2(0x18);
  code1(ip->opcode + d);
}


/* IN.4 [AC] */
void fin(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, d;

  dcdfield(&cp, &f, FT_N|FT_DEFLT|F_N|4);

  dcdarg(cp, 1);

  if(!type(GEN1, AP_RAC))
    {
      error_1incons_arg(ip);
      d = 0;
    }
  else
    d = (arg[0].reg == R_C);

  code2(0x08);
  code1(2+d);
}


/* OUT.[SX] C */
void fout(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f;

  dcdfield(&cp, &f, FT_FIX);

  dcdarg(cp, 1);

  if(!type(GEN1, AP_REG|R_C))
    error_1incons_arg(ip);

  if(f != F_S && f != F_X)
    error_incons_suffix(NULL);

  code2(0x08);
  code1(0 + (f == F_X));
}


/* {SLN|SRN}.f d */
void fshfn(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, d;

  dcdfield(&cp, &f, FT_FIX);

  dcdarg(cp, 1);

  if((d = types_table(s_1_table, 1, 4)) < 0)
    {
      error_1incons_arg(ip);
      d = 0;
    }

  codefield(f, code2, 0x8b, code1, 0xf);

  code1(ip->opcode + d);
}


/* SRB.f d */
void fsrb(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, d;

  dcdfield(&cp, &f, FT_FIX);

  dcdarg(cp, 1);

  if((d = types_table(s_1_table, 1, 4)) < 0)
    {
      error_1incons_arg(ip);
      d = 0;
    }

  code2(0x18);

  if(f == F_W)
    code1(0xc + d);
  else
    {
      code1(9);
      codefield(f, code1, 0, code1, 0xf);
      code1(d);
    }
}


/* SWAP.f s,d */
void fswap(ip, cp)
  struct istruct *ip;
  char *cp;
{
  int f, sd;

  dcdfield(&cp, &f, FT_FIX|FT_N|FT_DEFLT|F_DEFLT);

  dcdarg(cp, 2);

  /* C,ST or ST,C? */
  if(types2(AP_REG|R_C, AP_REG|R_ST) ||
     types2(AP_REG|R_ST, AP_REG|R_C))
    {
      if(f != F_DEFLT && f != F_X)
	error_incons_suffix(".X");

      code2(0xb0);
      return;
    }

  /* P,C.n or C.n,P? */
  if(types2(AP_REG|R_P, AP_TYPE|T_CN) ||
     types2(AP_TYPE|T_CN, AP_REG|R_P))
    {
      struct astruct *ap;

      /* Assert .1 or none */
      if(!((f & F_N) && (f & 077) == 1) &&
	 f != F_DEFLT)
	error_incons_suffix(".1");

      code3(0xf08);

      ap = (arg[0].type == T_CN ? GEN1 : GEN2);

      ap->type = T_IMM;
      chkimm(ap, 0L, 15L);

      code1(ap->disp1);
      return;
    }

  /* [AC],PC or PC,[AC]? */
  if(types2(AP_RAC, AP_REG|R_PC) ||
     types2(AP_REG|R_PC, AP_RAC))
    {
      struct astruct *apac;

      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");

      code3(0xb18);

      apac = (type(GEN1, AP_REG|R_PC) ? GEN2 : GEN1);

      code1(6 + (apac->reg == R_C));
      return;
    }


  /* [AC],Rn or Rn,[AC] ? */
  if(types2(AP_RAC, AP_RN) || types2(AP_RN, AP_RAC))
    {
      struct astruct *aprn, *apac;

      aprn = (type(GEN1, AP_RN) ? GEN1 : GEN2);
      apac = (aprn == GEN1 ? GEN2 : GEN1);

      /* .W or none? */
      if(f == F_DEFLT || f == F_W)
	{
	  code2(0x21);
	  code1((apac->reg == R_C ? 8 : 0) + aprn->reg - R_R0);
	  return;
	}

      code3(0xa18);
      codefield(f, code1, 0, code1, 0xf);
      code1(2);
      code1((apac->reg == R_C ? 8 : 0) + aprn->reg - R_R0);
      return;
    }

  /* [AC],Dn or Dn,[AC]? */
  if(types2(AP_RAC, AP_D01) || types2(AP_D01, AP_RAC))
    {
      struct astruct *apdn, *apac;
      int z = 2;

      apdn = (type(GEN1, AP_D01) ? GEN1 : GEN2);
      apac = (apdn == GEN1 ? GEN2 : GEN1);
      
      if(f != F_DEFLT && f != F_A && !((f & F_N) && (f & 077) == 4))
	error_incons_suffix(".A or .4");

      if((f & F_N) && (f & 077) == 4)
	z = 0xa;

      if(apac->reg == R_C)
	z += 4;

      z += apdn->reg - R_D0;
      
      code2(0x31);
      code1(z);
      return;
    }

  /* s,d or d,s? */
  if((sd = types_table(sd_1_table, 2, 8)) >= 0)
    {
      sd &= 3;

      if(f == F_DEFLT)
	{
	  sgnerr("Missing instruction suffix");
	  f = F_A;
	}

      codefield(f, code2, 0x8a, code1, 0xd);
      code1(0xc + sd);
      return;
    }

  error_incons_args(ip);
}


/* MOVE.f s,d */
void fmove(ip,cp)
  struct istruct *ip;
  char *cp;
{
  int f, sd;

  dcdfield(&cp, &f, FT_FIX|FT_N|FT_PN | FT_DEFLT|F_DEFLT);
  
  dcdarg(cp, 2);


  /* @[AC],PC? */
  if(types2(AP_TYPE|T_INDAC, AP_REG|R_PC))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");

      code3(0x808);
      code1(0xc + (arg[0].reg == R_C ? 2 : 0));
      return;
    }

  /* [AC],PC? */
  if(types2(AP_RAC, AP_REG|R_PC))
    {
      if(f != F_A && f != F_DEFLT)
	error_incons_suffix(".A");

      code3(0xb18);
      code1(2 + (arg[0].reg == R_C));
      return;
    }

  /* [ABCD],[ABCD]? */
  if((sd = types_table(sd_1_table, 2, 8)) >= 0)
    {
      if((f & F_N) || (f & F_PN))
	{
	  error_incons_suffix(NULL);
	  f = F_A;
	}

      if(f == F_DEFLT)
	{
	  sgnerr("Missing instruction suffix");
	  f = F_A;
	}

      codefield(f, code2, 0x8a, code1, 0xd);
      code1(4 + sd);
      return;
    }

  /* [AC],@D[01] or @D[01],[AC]? */
  if((sd = types_table(sacd01_table, 2, 8)) >= 0)
    {
      struct astruct *apdn, *apac;

      if(f == F_DEFLT)
	{
	  sgnerr("Missing instruction suffix");
	  f = F_A;
	}

      if(f & F_PN)
	{
	  error_incons_suffix(NULL);
	  f = F_A;
	}

      apdn = (type(GEN1, AP_ID01) ? GEN1 : GEN2);
      apac = (apdn == GEN1 ? GEN2 : GEN1);

      if(apdn->reg == R_D1)
	sd++;

      if(f == F_A)
	{
	  code2(0x41);
	  code1(sd);
	  return;
	}

      if(f == F_B)
	{
	  code2(0x41);
	  code1(8 + sd);
	  return;
	}

      code2(0x51);

      if(f & F_N)
	sd += 8;

      code1(sd);

      if(f & F_N)
	code1((f-1) & 0xf);
      else
	codefield(f, code1, 0, code1, 0);

      return;
    }

  /* [AC],D[01]? */
  if(types2(AP_RAC, AP_D01))
    {
      sd = 0;

      if(f == F_DEFLT)
	f = F_A;

      if(f != F_A && !((f & F_N) && (f & 077) == 4))
	{
	  error_incons_suffix(".A or .4");
	  f = F_A;
	}

      code2(0x31);

      /* .4? */
      if(f != F_A)
	sd = 8;

      code1(sd + (arg[0].reg == R_C ? 4 : 0) + (arg[1].reg == R_D1));
      return;
    }
	  
  /* [AC],R[0-4] or R[0-4],[AC]? */
  if(types2(AP_RAC, AP_RN) || types2(AP_RN, AP_RAC))
    {
      struct astruct *aprn, *apac;
      int r = 0;

      aprn = (type(GEN1, AP_RN) ? GEN1 : GEN2);
      apac = (aprn == GEN1 ? GEN2 : GEN1);

      if(f == F_DEFLT)
	f = F_W;

      if((f & F_N) || (f & F_PN))
	{
	  error_incons_suffix(NULL);
	  f = F_W;
	}

      sd = 0;

      if(aprn == GEN1)
	sd++;

      if(apac->reg == R_C)
	r = 8;

      /* .W? */
      if(f == F_W)
	{
	  code1(1);
	  code1(sd);
	  code1(r + aprn->reg - R_R0);
	  return;
	}

      code3(0xa18);
      codefield(f, code1, 0, code1, 0xf);
      code1(sd);
      code1(r + aprn->reg - R_R0);
      return;
    }

  /* PC,[AC]? */
  if(types2(AP_REG|R_PC, AP_RAC))
    {
      if(f == F_DEFLT)
	f = F_A;

      if(f != F_A)
	error_incons_suffix(".A");

      code3(0xb18);
      code1(4 + (arg[1].reg == R_C));
      return;
    }

  /* #imm,[AC] */
  if(types2(AP_TYPE|T_IMM, AP_RAC))
    {
      int n;

      if(!((f & F_PN) && (f & 0x1f) && (f & 0x1f) <= 16))
	{
	  error_incons_suffix(".P1 to .P16");
	  return;
	}

      if(arg[1].reg == R_C)
	code1(3);
      else
	code4(0x2808);

      n = ((f - 1) & 0xf) + 1;
      code1(n-1);
      
      chkimm(GEN1, - ((INT) 1 << (n * 4 - 1)), ((INT) 1 << (n * 4)) - 1);
      coden(n, arg[0].disp1);
      return;
    }

  /* #imm, D[01] */
  if(types2(AP_TYPE|T_IMM, AP_D01))
    {
      int n, op = 9;

      if(!(f == F_B || f == F_A) &&
	 (!(f & F_N) || !(f & 0xf) || (f & 0xf) > 8))
	{
	  error_incons_suffix(".B, .A, .2, .4, or .5");
	  return;
	}

      if(arg[1].reg == R_D1)
	op += 4;

      if(f == F_B || f == F_A)
	n = fnibbles(f);
      else
	n = ((f - 1) & 0xf) + 1;

      if(n != 2 && n != 4 && n != 5)
	{
	  error_incons_suffix(".B, .A, .2, .4, or .5");
	  return;
	}

      code1(1);
      code1(op + (n == 2 ? 0 : (n == 4 ? 1 : 2)));

      chkimm(GEN1, - ((INT) 1 << (n * 4 - 1)), ((INT) 1 << (n * 4)) - 1);

      coden(n, arg[0].disp1);
      return;
    }      


  /* #imm,P? */
  if(types2(AP_TYPE|T_IMM, AP_REG|R_P))
    {
      if(!((f & F_N) && (f & 0xf) == 1) && f != F_DEFLT)
	error_incons_suffix(".1");

      chkimm(GEN1, 0L, 15L);

      code1(2);
      code1(arg[0].disp1);
      return;
    }

	  
  /* P,C.n or C.n,P? */
  if((sd = types2(AP_REG|R_P, AP_TYPE|T_CN)) ||
     types2(AP_TYPE|T_CN, AP_REG|R_P))
    {
      struct astruct *acn;

      if(!((f & F_N) && (f & 0xf) == 1) && f != F_DEFLT)
	error_incons_suffix(".1");

      acn = (sd ? GEN2 : GEN1);

      code2(0x08);
      code1(0xc + !sd);
      code1(acn->disp1);
      return;
    }

  /* ST,C or C,ST? */
  if((sd = types2(AP_REG|R_ST, AP_REG|R_C)) ||
     types2(AP_REG|R_C, AP_REG|R_ST))
    {
      if(f != F_X && f != F_DEFLT)
	error_incons_suffix(".X");

      code1(0);
      code1(0xa - sd);
      return;
    }

  /* ID,C? */
  if(types2(AP_TYPE|T_ID, AP_REG|R_C))
    {
      if(f != F_X && f != F_A)
	error_incons_suffix(".A");

      code3(0x608);
      return;
    }

  error_incons_args(ip);
}
