/* forms.c -- STAR Formats

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, 1991 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"


/* Global variables */
extern errcnt, pass;


/* Local data */
char *mnam[] = {
  "*UNDEFINED*",
  "register",
  "immediate",
  "register indirect",
  "ID",
  "C nibble",
  "P+1"};


/* Return name of addressing mode */
static char *modename(argp)
  register struct astruct *argp;
{
  register int type;
  
  type = argp->type;
  
  /* Illegal mode? */
  if(type < T_REG || type > T_ID)
    return(mnam[0]);
  
  return(mnam[type]);
}


/* Make sure disp1 field is in selected range.
 * The range should contain 0.
 */
void assert_imm_range(argp, lolimit, hilimit)
  struct astruct *argp;
  INT lolimit;
  unsigned INT hilimit;
{
  /* Implicit isimm() */
  if(argp->type != T_IMM)
    {
      /* Display message */
      isimm(argp);

      argp->disp1 = lolimit;
      return;
    }

  /* Test range */
  if(hilimit && lolimit &&
     ((argp->disp1 > 0 && (unsigned INT) argp->disp1 > hilimit) ||
      (argp->disp1 < 0 && (INT) argp->disp1 < lolimit)))
    {
      /* Out of range -- signal error and set value to 0 */
      sgnerr("Argument value of 0x%lx out of range", /*  0x%lx to 0x%lx", */
	     (long) argp->disp1 /* , (long) lolimit, (long) hilimit */ );
      
      argp->disp1 = 0;
    }
}


/* Make sure INT is in selected range */
void assert_range(i, lolimit, hilimit)
  INT i, lolimit;
  unsigned INT hilimit;
{
  struct astruct a;

  a.type = T_IMM;
  a.disp1 = i;

  assert_imm_range(&a, lolimit, hilimit);
}


/* Make sure argument is of mode register
 * If not, make it A and signal error
 */
isanyreg(argp)
  register struct astruct *argp;
{
  if(argp->type == T_REG)
    return(TRUE);
  
  sgnerr("Must be register, not %s", modename(argp));
  
  argp->type = T_REG;
  argp->pres = 0;
  argp->reg  = 0;
  return(FALSE);
}


/* Make sure argument is of mode immediate
 * If not, make it #0 and signal error
 */
isimm(argp)
  register struct astruct *argp;
{
  if(argp->type == T_IMM)
    return(TRUE);
  
  sgnerr("Must be value, not %s", modename(argp));
  
  /* Make it #0 */
  argp->type = T_IMM;
  argp->disp1= 0;
  argp->pres = 0;
  return(FALSE);
}


/* Generate jump offset */
gendisp(disp, dispdiff, op3, op4)
  long disp;
  int dispdiff, op3, op4;
{
  register int qsav;
  
  /* Check maximum range */
  if((disp < -(long) 0x8000 || disp > (long) 0x7fff))
    {
      sgnwrn("Displacement of %lx out of 16-bit range", disp);
      disp = 0L;
    }
  
  /* Will it fit in 3 nibbles? */
  if(disp >= -0x800 && disp <= 0x7ff)
    {
      /* Sure, output opcode and offset */
      code1(op3);
      code3(disp); 
      return;
    }
  
  /* No, use four nibbles */
  disp += dispdiff;

  code2(op4);
  code4(disp);
}


/* Reverse bits in byte */
revbits(mask)
  unsigned int mask;
{
  static unsigned int i;
  register unsigned int mask1,mask2;
  
  for(mask1 = mask, i = mask2 = 0; i<8; i++)
    {
      mask2 <<= 1;
      mask2 |= (mask1 & 1);
      mask1 >>= 1;
    }
  
  return(mask2);
}
