/* onoff_common.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Port                                                                   */
/*                                                                           */
/* ON/OFF flags used by several targets                                      */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "cmdarg.h"
#include "onoff_common.h"

Boolean FPUAvail,    /* floating point co processor instructions allowed? */
        PMMUAvail,   /* PMMU instructions allowed? */
        SupAllowed,  /* supervisor mode enabled */
        MaxMode,     /* CPU in maximum mode */
        CompMode,    /* Enable Compatibility Mode */
        TargetBigEndian, /* Data storage Big Endian? */
        DoPadding,   /* align to even address? */
        Packing,     /* packed data storage? */
        default_regsyms, /* built-in default register symbols? */
        extra_comments;
static unsigned registered, ext_registered;

#define SupAllowedCmdName "SUPMODE"   /* Privileged instructions allowed */
#define SupAllowedSymName "INSUPMODE"
static Boolean DefSupAllowed;

#define BigEndianCmdName  "BIGENDIAN"
#define BigEndianSymName  "BIGENDIAN"  /* Data storage MSB first */
static Boolean DefBigEndian;

#define CompModeCmdName   "COMPMODE"   /* Compatibility Mode */
#define CompModeSymName   "COMPMODE"
static Boolean DefCompMode;

#define PackingCmdName    "PACKING"
#define PackingSymName    "PACKING"
static Boolean DefPacking, DefPackingSet;

#define extra_comments_name "EXTRACOMMENTS"
static Boolean def_extra_comments;

/*!------------------------------------------------------------------------
 * \fn     onoff_test_and_set(unsigned mask)
 * \brief  check whether on/off insn is registered first time during pass
 * \param  mask insn to test
 * \return mask if first time, otherwise 0
 * ------------------------------------------------------------------------ */

unsigned onoff_test_and_set(unsigned mask)
{
  unsigned curr = registered;
  registered |= mask;
  return curr & mask;
}

unsigned onoff_ext_test_and_set(unsigned mask)
{
  unsigned curr = ext_registered;
  ext_registered |= mask;
  return curr & mask;
}

/*!------------------------------------------------------------------------
 * \fn     onoff_fpu_add(void)
 * \brief  register on/off command to enable/disable FPU instruction extensions
 * ------------------------------------------------------------------------ */

void onoff_fpu_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_fpu))
    SetFlag(&FPUAvail, FPUAvailName, False);
  AddONOFF("FPU", &FPUAvail, FPUAvailName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_pmmu_add(void)
 * \brief  register on/off command to enable/disable PMMU instruction extensions
 * ------------------------------------------------------------------------ */

void onoff_pmmu_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_pmmu))
    SetFlag(&PMMUAvail, PMMUAvailName, False);
  AddONOFF("PMMU", &PMMUAvail, PMMUAvailName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_supmode_add(void)
 * \brief  register on/off command to enable/disable supervisor mode-only instructions
 * ------------------------------------------------------------------------ */

void onoff_supmode_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_supmode))
    SetFlag(&SupAllowed, SupAllowedSymName, DefSupAllowed);
  AddONOFF(SupAllowedCmdName, &SupAllowed, SupAllowedSymName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_maxmode_add(void)
 * \brief  register on/off command to enable/disable maximum mode-only instructions
 * ------------------------------------------------------------------------ */

void onoff_maxmode_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_maxmode))
    SetFlag(&MaxMode, MaxModeSymName, False);
  AddONOFF(MaxModeCmdName, &MaxMode, MaxModeSymName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_compmode_add(void)
 * \brief  register on/off command to enable/disable compatibility mode
 * ------------------------------------------------------------------------ */

void onoff_compmode_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_compmode))
    SetFlag(&CompMode, CompModeSymName, DefCompMode);
  AddONOFF(CompModeCmdName, &CompMode, CompModeSymName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_bigendian_add(void)
 * \brief  register on/off command to set big/little endian data storage
 * ------------------------------------------------------------------------ */

void onoff_bigendian_add(void)
{
  if (!onoff_test_and_set(e_onoff_reg_bigendian))
    SetFlag(&TargetBigEndian, BigEndianSymName, DefBigEndian);
  AddONOFF(BigEndianCmdName, &TargetBigEndian, BigEndianSymName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_packing_add(Boolean def_value)
 * \brief  register on/off command to set packed data storage
 * \param  def_value to set as default
 * ------------------------------------------------------------------------ */

void onoff_packing_add(Boolean def_value)
{
  if (!onoff_test_and_set(e_onoff_reg_packing))
    SetFlag(&Packing, PackingSymName, DefPackingSet ? DefPacking : def_value);
  AddONOFF(PackingCmdName, &Packing, PackingSymName, False);
}

/*!------------------------------------------------------------------------
 * \fn     onoff_extra_comments_add(void)
 * \brief  register on/off command to enable/disable extra comments
 * ------------------------------------------------------------------------ */

void onoff_extra_comments_add(void)
{
  if (!onoff_ext_test_and_set(e_onoff_ext_reg_extra_comments))
    SetFlag(&extra_comments, extra_comments_name, def_extra_comments);
  AddONOFF(extra_comments_name, &extra_comments, extra_comments_name, False);
}

/*!------------------------------------------------------------------------
 * \fn     initpass_onoff(void)
 * \brief  per-pass initialization of module
 * ------------------------------------------------------------------------ */

static void initpass_onoff(void)
{
  registered =
  ext_registered = 0;
}

/*!------------------------------------------------------------------------
 * \fn     onoff_common_init(void)
 * \brief  module initialization
 * ------------------------------------------------------------------------ */

static as_cmd_result_t CMD_SupAllowed(Boolean Negate, const char *pArg)
{
  UNUSED(pArg);

  DefSupAllowed = !Negate;
  return e_cmd_ok;
}

static as_cmd_result_t CMD_BigEndian(Boolean Negate, const char *pArg)
{
  UNUSED(pArg);

  DefBigEndian = !Negate;
  return e_cmd_ok;
}

static as_cmd_result_t CMD_CompMode(Boolean Negate, const char *Arg)
{
  UNUSED(Arg);

  DefCompMode = !Negate;
  return e_cmd_ok;
}

static as_cmd_result_t CMD_Packing(Boolean Negate, const char *Arg)
{
  UNUSED(Arg);

  DefPacking = !Negate;
  DefPackingSet = True;
  return e_cmd_ok;
}

static as_cmd_result_t cmd_extra_comments(Boolean negate, const char *p_arg)
{
  UNUSED(p_arg);

  def_extra_comments = !negate;
  return e_cmd_ok;
}

static const as_cmd_rec_t onoff_params[] =
{
  { BigEndianCmdName  , CMD_BigEndian       },
  { CompModeCmdName   , CMD_CompMode        },
  { PackingCmdName    , CMD_Packing         },
  { SupAllowedCmdName , CMD_SupAllowed      },
  { extra_comments_name, cmd_extra_comments }
};

extern void onoff_common_init(void)
{
  as_cmd_register(onoff_params, as_array_size(onoff_params));
  DefSupAllowed = False;
  DefBigEndian = False;
  DefCompMode = False;
  DefPacking = DefPackingSet = False;
  extra_comments = False;
  AddInitPassProc(initpass_onoff);
}
