/*
 * ACME - a crossassembler for producing 6502/65c02/65816 code.
 * Copyright (C) 1998 Marco Baye
 * Have a look at "acme.c" for further info
 */

/*
 * Pseudo opcodes
 */

#include "po.h"

/*
 * Switch to short accu
 */
static void FN_PO_as() {
  fCPU_LongA  = FALSE;
  fCPU_LongAb = FALSE;
  FN_EnsureEOL();
}

/*
 * Switch to long accu
 */
static void FN_PO_al() {
  if(hCPU_Now == HCPU_65816) {
    fCPU_LongA  = TRUE;
    fCPU_LongAb = TRUE;
  } else FN_Message(pseWrongCPU, EERROR);
  FN_EnsureEOL();
}

/*
 * Switch to short index registers
 */
static void FN_PO_rs() {
  fCPU_LongR  = FALSE;
  fCPU_LongRb = FALSE;
  FN_EnsureEOL();
}

/*
 * Switch to long index registers
 */
static void FN_PO_rl() {
  if(hCPU_Now == HCPU_65816) {
    fCPU_LongR  = TRUE;
    fCPU_LongRb = TRUE;
  } else FN_Message(pseWrongCPU, EERROR);
  FN_EnsureEOL();
}

/*
 * Insert 8-bit values
 */
static void FN_PO_08() {
  do {
    FN_Stream_PutByte(FN_ALU_GetValue_Medium());
  } while(FN_Stream_Comma());
  FN_EnsureEOL();
}

/*
 * Insert 16-bit values
 */
static void FN_PO_16() {
  Value v;

  do {
    v = FN_ALU_GetValue_Medium();
    FN_Stream_PutByte(v & 255);
    FN_Stream_PutByte(v >> 8);
  } while(FN_Stream_Comma());
  FN_EnsureEOL();
}

/*
 * Insert 24-bit values
 */
static void FN_PO_24() {
  Value v;

  do {
    v = FN_ALU_GetValue_Medium();
    FN_Stream_PutByte(v & 255);
    FN_Stream_PutByte((v >> 8) & 255);
    FN_Stream_PutByte(v >> 16);
  } while(FN_Stream_Comma());
  FN_EnsureEOL();
}

/*
 * Reserve space by sending some bytes of a given value
 */
static void FN_PO_fill() {
  Value v    = VFILL;
  int   Size = FN_ALU_GetValue_Strict();

  if(FN_Stream_Comma()) v = FN_ALU_GetValue_Medium();
  FN_EnsureEOL();
  while(Size) {
    FN_Stream_PutByte(v);
    Size--;
  }
}

/*
 * Select dump file
 */
static void FN_PO_savelabels() {
  int len = FN_Stream_ReadFilename(pnfDump, LNFMAX, FALSE);

  if(len) {
    ffProgram |= FDOLABELDUMP;/* set flag for dump */
    FN_EnsureEOL();
  } else {
    FN_SkipRest();
  }
}

/*
 * Select output file
 */
static void FN_PO_to() {
  int len;

  if(ffPass & FPASSDOONCE) {
    /* in first pass, act upon this */
    if(ffProgram & FOUTFILECHOSEN) {
      /* if outfile already chosen, complain */
      FN_Message(pseNo2ndOutfile, EERROR);
    } else {
      /* do some work */
      ffProgram |= FOUTFILECHOSEN;/* remember by setting flag */
      len = FN_Stream_ReadFilename(pnfOut, LNFMAX, FALSE);
      if(len) {
        /* !!! accept file format value !!! */
        FN_EnsureEOL();
      } else {
        FN_SkipRest();
      }
    }
  } else {
    /* in other passes, just ignore it */
    FN_SkipRest();
  }
}

/*
 * Select CPU
 */
static void FN_PO_cpu() {
  ListItem *p;
  char      handle;
  int       len;

  SKIPSPACE;
  len = FN_Stream_ReadKeyword(&StringItem.String[0], LSMAX, FALSE);
  if(len) {
    /* search for list item, length = type + string */
    p = FN_Struct_Search(&StringItem, HTYPE_CPU, len + 1, 0);
    if(p) {
      handle = p->Data.Bytes.VALUE;/* get handle */
      hCPU_Now = handle;
      if(handle == HCPU_65816) {
        fCPU_LongA = fCPU_LongAb;/* use old values */
        fCPU_LongR = fCPU_LongRb;
      } else {
        fCPU_LongA = FALSE;/* other processors can't do it */
        fCPU_LongR = FALSE;
      }
    } else FN_Message(pseUkCPU, EERROR);
  }
  FN_EnsureEOL();
}

/*
 * Include binary file
 */
static void FN_PO_binary() {
  int   byte;
  Value size;
  int   len;

  /* Check for number of files is done in FN_Stream_OpenSub() */
  len = FN_Stream_ReadFilename(pPathname, LNPMAX, TRUE);
  if(len) {
    if(FN_Stream_Comma()) {
      /* explicit size info */
      size = FN_ALU_GetValue_Strict();
      /* include file */
      if(FN_Stream_OpenSub(pPathname)) {
        while(size) {
          byte = fgetc(hfSub);
          if(byte == EOF) byte = 0;
          FN_Stream_PutByte(byte);
          size--;
        }
        FN_Stream_CloseSub();
      } else {
        PC_inc += size;/* add size to PC */
      }
    } else {
      /* no explicit file info, so read file in every pass */
      if(FN_Stream_OpenSub(pPathname)) {
        do {
          byte = fgetc(hfSub);
          if(byte != EOF) FN_Stream_PutByte(byte);
        } while(byte != EOF);
        FN_Stream_CloseSub();
      }
    }
    FN_EnsureEOL();
  } else {
    FN_SkipRest();
  }
}

/*
 * Insert NOPs until PC fits conditions
 */
static void FN_PO_align() {
  unsigned char v    = VALIGN;
  Value         Equal,
                Test = PC_CPU,
                And  = FN_ALU_GetValue_Strict();

  if(fPCdefined) {
    if(!FN_Stream_Comma()) FN_Message(pseSyntax, EERROR);
    Equal = FN_ALU_GetValue_Strict();
    if(FN_Stream_Comma()) v = FN_ALU_GetValue_Medium();
    FN_EnsureEOL();
    while((Test & And) != Equal) {
      FN_Stream_PutByte(v);
      Test++;
    }
  } else FN_Message(pseNoPC, ESERIOUS);
}

/*
 * Switch to CBM mode
 */
static void FN_PO_cbm() {
  hCodeTable_Now = HCODETABLE_PET;
  /* !!! extend "!cbm" to do other stuff like file formats !!! */
  /* not anymore - take it out and replace with library file */
  FN_EnsureEOL();
}

/*
 * Insert text string (default format)
 */
static void FN_PO_text() {
  FN_PO_string(hCodeTable_Now, 0);
}

/*
 * Insert PetSCII string
 */
static void FN_PO_petscii() {
  FN_PO_string(HCODETABLE_PET, 0);
}

/*
 * Insert raw string
 */
static void FN_PO_raw() {
  FN_PO_string(HCODETABLE_RAW, 0);
}

/*
 * Insert screencode string
 */
static void FN_PO_screencode() {
  FN_PO_string(HCODETABLE_SCR, 0);
}

/*
 * Insert screencode string, EOR'd
 */
static void FN_PO_scrxor() {
  Value eor = FN_ALU_GetValue_Medium();

  if(FN_Stream_Comma()) {
    FN_PO_string(HCODETABLE_SCR, (char) eor);
  } else FN_Message(pseSyntax, EERROR);
}

/*
 * Insert string
 */
static void FN_PO_string(int hCT, char eor) {
  int a,
      len,
      temp = hCodeTable_Now;/* buffer current conversion table */

  SKIPSPACE;
  hCodeTable_Now = hCT;/* make given conversion table current one */
  do {
    if(GotByte == '"') {
      /* parse string */
      len = FN_Stream_FinishQuoted(&StringItem.String[0], LSMAX, 0);
      if(len) {
        FN_ConvertString(&StringItem.String[0], len, hCodeTable_Now);
        for(a = 0; a < len; a++) {
          FN_Stream_PutByte(StringItem.String[a] ^ eor);
        }
      } else {
        FN_SkipRest();/* error */
      }
    } else {
      /* Parse value. No problems with single characters because the current */
      /* conversion table is temporarily set to the given one. */
      FN_Stream_PutByte(FN_ALU_GetValue_Medium());
    }
  } while(FN_Stream_Comma());
  FN_EnsureEOL();
  hCodeTable_Now = temp;/* reactivate buffered conversion table */
}

/*
 * (Re)set label
 */
static void FN_PO_set() {
  ListItem *p;
  int       len,
            pf;
  Sixteen   Zone = NZONE_GLOBAL;

  SKIPSPACE;
  if(GotByte == '.') {
    Zone = Context[nContext].nZone_Now;
    FN_GetByte();
  }
  len = FN_Stream_ReadKeyword(&StringItem.String[2], LSMAX - 2, FALSE);
  /* (now: GotByte = illegal char) */
  if(len) {
    pf = FN_Mnemo_GetPostfix();/* read length info (skips spaces) */
    p = FN_Struct_GetPreparedLabel(Zone, len, pf);
    if(GotByte == '=') {
      /* label = parsed value */
      FN_GetByte();/* proceed with next char */
      FN_SetLabelValue(p, FN_ALU_GetValue_Medium(), TRUE);
      p->Data.Bytes.FLAGS |= (ffValue & (MVALUE_UNSURE | MVALUE_DEFINED));
      FN_EnsureEOL();
    } else {
      FN_Message(pseSyntax, EERROR);
      FN_SkipRest();
    }
  }
}
