/*
 *      pseudo --- pseudo op processing
 */

#define RMB     0               /* Reserve Memory Bytes */
#define FCB     1               /* Form Constant Bytes */
#define FDB     2               /* Form Double Bytes (words) */
#define FCC     3               /* Form Constant Characters */
#define ORG     4               /* Origin */
#define EQU     5               /* Equate */
#define ZMB     6               /* Zero memory bytes */
#define FILL    7               /* block fill constant bytes */
#define OPT     8               /* assembler option */
#define PAGE    9               /* new page */
#define CRC32   10              /* confirm crc of file to patch */
#define NAME    11              /* output name for patched file */
#define BASENAME    12          /* output base name for patched file */
#define PATCHNAME   13          /* output name modifier for patched file */
#define LENGTH  14              /* check length of file to patch */
#define NULL_OP 15              /* null pseudo op */

/* Unfortunately the symbol table manager requires this to be in alphabetical order */
struct oper pseudo[] = {
#ifdef VPATCH
  {"bas", PSEUDO, BASENAME, 0},
  {"basename", PSEUDO, BASENAME, 0},
#endif
  {"bsz", PSEUDO, ZMB, 0},
#ifdef VPATCH
  {"crc", PSEUDO, CRC32, 0},
  {"crc32", PSEUDO, CRC32, 0},
#endif
  {"end", PSEUDO, NULL_OP, 0},
  {"equ", PSEUDO, EQU, 0},
  {"fcb", PSEUDO, FCB, 0},
  {"fcc", PSEUDO, FCC, 0},
  {"fdb", PSEUDO, FDB, 0},
  {"fill", PSEUDO, FILL, 0},
#ifdef VPATCH
  {"len", PSEUDO, LENGTH, 0},
  {"length", PSEUDO, LENGTH, 0},
  {"nam", PSEUDO, NAME, 0},
  {"name", PSEUDO, NAME, 0},
#else
  {"nam", PSEUDO, NULL_OP, 0},
  {"name", PSEUDO, NULL_OP, 0},
#endif
  {"opt", PSEUDO, OPT, 0},
  {"org", PSEUDO, ORG, 0},
  {"pag", PSEUDO, PAGE, 0},
  {"page", PSEUDO, PAGE, 0},
#ifdef VPATCH
  {"patch", PSEUDO, PATCHNAME, 0},
  {"pch", PSEUDO, PATCHNAME, 0},
#endif
  {"rmb", PSEUDO, RMB, 0},
  {"spc", PSEUDO, NULL_OP, 0},
  {"ttl", PSEUDO, NULL_OP, 0},
  {"zmb", PSEUDO, ZMB, 0}
};

/*
 *      do_pseudo --- do pseudo op processing
 */
void do_pseudo (int op)
/* which op */
{
  char fccdelim;
  int fill;
  static char PatchNameStr[MAXPATH];
  char *PatchName = PatchNameStr;
  int CheckCRC32 = 0;

  if (op != EQU && *Label)
    install (Label, Pc);

  P_force++;
  switch (op) {
#ifdef VPATCH
  case NAME: /* Deprecated! */
    if (*Operand == EOS)
      break;
    fccdelim = *Optr++;
    if (fccdelim != '"') {
      error ("Missing quotes around \"%s\"", Operand);
    } else {
      while ((*Optr != EOS) && (*Optr != fccdelim)) {
        if ((PatchName-PatchNameStr) > (MAXPATH-3)) fatal ("Patch name in 'name' directive is too long!\n");
        *PatchName++ = *Optr++;
      }
      if (*Optr == fccdelim) {
        Optr++;
        *PatchName = '\0';
        strcpy(Patch_name, PatchNameStr);
        debug ("Patching %s to %s%s.vec", Patch_Binary, Patch_name, Patched_output_name);
      } else {
        error ("Missing closing quote in %s", Operand);
      }
    }
    break;

  case BASENAME:  /* Replace bin_name with this */
    if (*Operand == EOS)
      break;
    fccdelim = *Optr++;
    if (fccdelim != '"') {
      error ("Missing quotes around \"%s\"", Operand);
    } else {
      while ((*Optr != EOS) && (*Optr != fccdelim)) {
        if ((PatchName-PatchNameStr) > (MAXPATH-3)) fatal ("Patch name in 'name' directive is too long!\n");
        *PatchName++ = *Optr++;
      }

/*
as.c:    Fd = fopen (Patch_Binary, "rb");
as.c:    if ((Fd = fopen (*++np, "r")) != NULL) {
as.c:    if ((Fd = fopen (*++np, "r")) != NULL) { make_pass (); fclose (Fd); }
as.c:    if ((Objfil = fopen (Obj_name, "w")) == NULL) fatal ("Can't create object file");
as.c:    if ((Binfil = fopen (Bin_name, "w")) == NULL) fatal ("Can't create binary file");
as.c:    if ((Lstfil = fopen (Lst_name, "w")) == NULL) fatal ("Can't create list file");
as.c:    if ((Symfil = fopen (Sym_name, "w")) == NULL) fatal ("Can't create symbol file");
as.c:    if ((Crffil = fopen (Crf_name, "w")) == NULL) fatal ("Can't create cross reference file");
as.c:  Patchedfil = fopen (Patched_output_name, "wb");
*/
      
      if (*Optr == fccdelim) {
        Optr++;
        *PatchName = '\0';
        strcpy(Patch_name, PatchNameStr); /* Replace any previous base name */
        debug ("Patching %s to %s%s.vec", Patch_Binary, Patch_name, Patched_output_name);
      } else {
        error ("Missing closing quote in %s", Operand);
      }
    }
    break;

  case PATCHNAME:
    if (*Operand == EOS)
      break;
    fccdelim = *Optr++;
    if (fccdelim != '"') {
      error ("Missing quotes around \"%s\"", Operand);
    } else {
      while ((*Optr != EOS) && (*Optr != fccdelim)) {
        if ((PatchName-PatchNameStr) > (MAXPATH-3)) fatal ("Patch name in 'name' directive is too long!\n");
        *PatchName++ = *Optr++;
      }

      if (*Optr == fccdelim) {
        Optr++;
        *PatchName = '\0';
        if (Pass == 1) {
          strcat(Patched_output_name, "-");
          strcat(Patched_output_name, PatchNameStr);
        }
        debug ("Patching %s to %s%s.vec", Patch_Binary, Patch_name, Patched_output_name);
      } else {
        error ("Missing closing quote in %s", Operand);
      }
    }
    break;

  case LENGTH:
    if (eval ()) {
      CheckLength = Result;
      debug ("Checking that file length is at least %d before being patched", CheckLength);
      if (ROM_Size < CheckLength) {
        fatal ("The file to be patched must be at least %d bytes, but it is only %d.", CheckLength, ROM_Size);
      } else {
        info ("Length is sufficient - safe to patch. ($%x)", CheckLength);
      }
    } else
      error ("Undefined Operand during Pass One");
    break;
    
  case CRC32:

    if (eval ()) {
      CheckCRC32 = Result;
      if (ROM_CRC32 != CheckCRC32) {
        warn ("CRCs do not match!  Expected $%x - got $%x", CheckCRC32, ROM_CRC32);
      } else {
        info ("CRCs match - safe to patch. ($%x)", CheckCRC32);
      }
    } else
      error ("Undefined Operand during Pass One");
    break;
#endif
  case RMB:                    /* reserve memory bytes */
    if (eval ()) {
      Pc += Result;
      f_record ();              /* flush out bytes */
    } else
      error ("Undefined Operand during Pass One");
    break;
  case ZMB:                    /* zero memory bytes */
    if (eval ())
      while (Result--)
        emit (0);
    else
      error ("Undefined Operand during Pass One");
    break;
  case FILL:                   /* fill memory with constant */
    eval ();
    fill = Result;
    if (*Optr++ != ',')
      error ("Bad fill");
    else {
      Optr = skip_white (Optr);
      eval ();
      while (Result--)
        emit (fill);
    }
    break;
  case FCB:                    /* form constant byte(s) */
    do {
      Optr = skip_white (Optr);
      eval ();
      if (Result > 0xFF) {
        if (!Force_byte)
          warn ("Value truncated");
        Result = lobyte (Result);
      }
      emit (Result);
    } while (*Optr++ == ',');
    Optr -= 1;
    break;
  case FDB:                    /* form double byte(s) */
    do {
      Optr = skip_white (Optr);
      eval ();
      eword (Result);
    } while (*Optr++ == ',');
    Optr -= 1;
    break;
  case FCC:                    /* form constant characters */
    if (*Operand == EOS)
      break;
    fccdelim = *Optr++;
    while (*Optr != EOS && *Optr != fccdelim)
      emit (*Optr++);
    if (*Optr == fccdelim)
      Optr++;
    else
      error ("Missing Delimiter");
    break;
  case ORG:                    /* origin */
    if (eval ()) {
      Old_pc = Pc = Result;
      f_record ();              /* flush out any bytes */
    } else
      error ("Undefined Operand during Pass One");
    break;
  case EQU:                    /* equate */
    if (*Label == EOS) {
      error ("EQU requires label");
      break;
    }
    if (eval ()) {
      install (Label, Result);
      Old_pc = Result;          /* override normal */
    } else
      error ("Undefined Operand during Pass One");
    break;
  case OPT:                    /* assembler option */
    P_force = 0;
    if (head (Operand, "l")) {
      Lflag = 1;
      Optr++;
    } else if (head (Operand, "nol")) {
      Lflag = 0;
      Optr += 3;
    } else if (head (Operand, "c")) {
      Cflag = 1;
      Ctotal = 0;
      Optr++;
    } else if (head (Operand, "noc")) {
      Cflag = 0;
      Optr += 3;
    } else if (head (Operand, "contc")) {
      Cflag = 1;
      Optr += 5;
    } else if (head (Operand, "s")) {
      Sflag = 1;
      Optr++;
    } else if (head (Operand, "cre")) {
      Rflag = 1;
      Optr += 3;
    } else if (head (Operand, "bin")) {
      Bflag = 1;
      Optr += 3;
    } else if (head (Operand, "s19")) {
      Oflag = 1;
      Optr += 3;
    } else
      error ("Unrecognized OPT %s", Operand);
    break;

  case PAGE:                   /* go to a new page */
    P_force = 0;
    N_page = 1;
    if (Pass == 2)
      if (Lflag) {
        fprintf (Lstfil, "\f");
        fprintf (Lstfil, "%-10s", Argv[Cfn]);
        fprintf (Lstfil, "                                   ");
        fprintf (Lstfil, "page %3d\n", Page_num++);
      }
    break;
  case NULL_OP:                /* ignored psuedo ops */
    P_force = 0;
    break;
  default:
    fatal ("Pseudo error");
  }
}
