/* Implements special instruction mnemonics
   Copyright (C) 2001, 2002, 2003, 2004, 2005
   Craig Franklin

This file is part of gputils.

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

gputils 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 gputils; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#include "stdhdr.h"

#include "libgputils.h"
#include "gpasm.h"
#include "gperror.h"
#include "special.h"
#include "directive.h"
#include "evaluate.h"

extern struct pnode *mk_constant(int value);
extern struct pnode *mk_list(struct pnode *head, struct pnode *tail);

struct pnode *make_constant_list(int value1, int value2)
{
  return mk_list(mk_constant(value1), mk_list(mk_constant(value2), NULL));
}

struct pnode *add_symbol_constant(struct pnode *parms, int value)
{
  return mk_list(HEAD(parms), mk_list(mk_constant(value), NULL));
}

/**************************************************************************/

static gpasmVal do_addcf(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  gpmessage(GPM_SPECIAL_MNEMONIC, NULL);

  do_insn("btfsc", make_constant_list(3, 0));
  do_insn("incf", parms);

  return r;
}

static gpasmVal do_adddcf(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 1));
  do_insn("incf", parms);

  return r;
}

static gpasmVal do_b(gpasmVal r,
		     char *name,
		     int arity,
		     struct pnode *parms)
{

  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bc(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 0));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bdc(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 1));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bz(gpasmVal r,
		      char *name,
		      int arity,
		      struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 2));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bnc(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{

  do_insn("btfss", make_constant_list(3, 0));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bndc(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  do_insn("btfss", make_constant_list(3, 1));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_bnz(gpasmVal r,
		       char *name,
		       int arity,
		       struct pnode *parms)
{

  do_insn("btfss", make_constant_list(3, 2));
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_clrc(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("bcf", make_constant_list(3, 0));

  return r;
}

static gpasmVal do_clrdc(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("bcf", make_constant_list(3, 1));

  return r;
}

static gpasmVal do_clrz(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("bcf", make_constant_list(3, 2));

  return r;
}

static gpasmVal do_lcall(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  do_insn("pagesel", parms);
  do_insn("call", parms);

  return r;
}

static gpasmVal do_lgoto(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  do_insn("pagesel", parms);
  do_insn("goto", parms);

  return r;
}

static gpasmVal do_movfw(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (enforce_arity(arity, 1)) {
    do_insn("movf", add_symbol_constant(parms, 0));
  } 

  return r;
}

static gpasmVal do_negf(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if ((arity == 1) || (arity == 2)) {
    do_insn("comf", add_symbol_constant(parms, 1));
    do_insn("incf", parms);
  } else {
    enforce_arity(arity, 2);
  }

  return r;
}

static gpasmVal do_setc(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }
  
  do_insn("bsf", make_constant_list(3, 0));

  return r;
}

static gpasmVal do_setdc(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("bsf", make_constant_list(3, 1));

  return r;
}

static gpasmVal do_setz(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("bsf", make_constant_list(3, 2));

  return r;
}

static gpasmVal do_skpc(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfss", make_constant_list(3, 0));

  return r;
}

static gpasmVal do_skpdc(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfss", make_constant_list(3, 1));

  return r;
}

static gpasmVal do_skpz(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfss", make_constant_list(3, 2));

  return r;
}

static gpasmVal do_skpnc(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfsc", make_constant_list(3, 0));

  return r;
}

static gpasmVal do_skpndc(gpasmVal r,
		          char *name,
		          int arity,
		          struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfsc", make_constant_list(3, 1));

  return r;
}

static gpasmVal do_skpnz(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  if (arity) {
    gperror(GPE_TOO_MANY_ARGU, NULL);
  }

  do_insn("btfsc", make_constant_list(3, 2));

  return r;
}

static gpasmVal do_subcf(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 0));
  do_insn("decf", parms);

  return r;
}

static gpasmVal do_subdcf(gpasmVal r,
		         char *name,
		         int arity,
		         struct pnode *parms)
{

  do_insn("btfsc", make_constant_list(3, 1));
  do_insn("decf", parms);

  return r;
}

static gpasmVal do_tstf(gpasmVal r,
		        char *name,
		        int arity,
		        struct pnode *parms)
{

  if (enforce_arity(arity, 1)) {
    do_insn("movf", add_symbol_constant(parms, 1));
  }

  return r;
}

/* PIC 12-bit and 14-bit "Special" instruction set */
struct insn special[] = {
  { "addcf",    0, (long int)do_addcf,  INSN_CLASS_FUNC,        0 },
  { "adddcf",   0, (long int)do_adddcf, INSN_CLASS_FUNC,        0 },
  { "b",        0, (long int)do_b,      INSN_CLASS_FUNC,        0 },
  { "bc",       0, (long int)do_bc,     INSN_CLASS_FUNC,        0 },
  { "bdc",      0, (long int)do_bdc,    INSN_CLASS_FUNC,        0 },
  { "bz",       0, (long int)do_bz,     INSN_CLASS_FUNC,        0 },
  { "bnc",      0, (long int)do_bnc,    INSN_CLASS_FUNC,        0 },
  { "bndc",     0, (long int)do_bndc,   INSN_CLASS_FUNC,        0 },
  { "bnz",      0, (long int)do_bnz,    INSN_CLASS_FUNC,        0 },
  { "clrc",     0, (long int)do_clrc,   INSN_CLASS_FUNC,        0 },
  { "clrdc",    0, (long int)do_clrdc,  INSN_CLASS_FUNC,        0 },
  { "clrz",     0, (long int)do_clrz,   INSN_CLASS_FUNC,        0 },
  { "lcall",    0, (long int)do_lcall,  INSN_CLASS_FUNC,        0 },
  { "lgoto",    0, (long int)do_lgoto,  INSN_CLASS_FUNC,        0 },
  { "movfw",    0, (long int)do_movfw,  INSN_CLASS_FUNC,        0 },
  { "negf",     0, (long int)do_negf,   INSN_CLASS_FUNC,        0 },
  { "setc",     0, (long int)do_setc,   INSN_CLASS_FUNC,        0 },
  { "setdc",    0, (long int)do_setdc,  INSN_CLASS_FUNC,        0 },
  { "setz",     0, (long int)do_setz,   INSN_CLASS_FUNC,        0 }, 
  { "skpc",     0, (long int)do_skpc,   INSN_CLASS_FUNC,        0 },
  { "skpdc",    0, (long int)do_skpdc,  INSN_CLASS_FUNC,        0 },
  { "skpz",     0, (long int)do_skpz,   INSN_CLASS_FUNC,        0 },
  { "skpnc",    0, (long int)do_skpnc,  INSN_CLASS_FUNC,        0 },
  { "skpndc",   0, (long int)do_skpndc, INSN_CLASS_FUNC,        0 },
  { "skpnz",    0, (long int)do_skpnz,  INSN_CLASS_FUNC,        0 },
  { "subcf",    0, (long int)do_subcf,  INSN_CLASS_FUNC,        0 },
  { "subdcf",   0, (long int)do_subdcf, INSN_CLASS_FUNC,        0 },
  { "tstf",     0, (long int)do_tstf,   INSN_CLASS_FUNC,        0 }

};

const int num_op_special = TABLE_SIZE(special);


syntax highlighted by Code2HTML, v. 0.9.1