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

/*
 * Arithmetic/logic unit
 */

#include "alu.h"

/*
 * These routines handle numerical expressions. There are operators for
 * arithmetic, logic, shift and comparison operations.
 * The core of this is the "FN_ALU_PVR" routine, it will recursively call
 * itself to find out the second operand when needed. Its parameter is the
 * priority value of the current operator; all operations with higher
 * priorities are done on the new level. When an illegal character is read or
 * when an operator has a priority lower than the given parameter, the routine
 * returns to the old level.
 * The global variable "ffValue" holds some information about the parsed value:
 *  -A label was referenced that had its "unsure"-flag set
 *  -A definite solution was achieved (no "undefined"-label was referenced)
 *  -The value's size (unknown, 1, 2 or 3 bytes)
 *  -An operation was done on the uppermost recursion level (VP_Work)
 * "unsure" is needed for producing the same addresses in all passes; because
 * in the first pass there will almost for sure be labels that are undefined,
 * you can't simply get the addressing mode from looking at the parameter's
 * value.
 * "defined" shows that the value really could be computed - so if an undefined
 * label was referenced, this flag will be cleared.
 * "VP_Work" is needed to recognize unnecessary parentheses (which imply use of
 * indirect adressing modes).
 */

/*
 * There are three different ways to call the core routine:
 *   FN_ALU_GetValue_Liberal(): allows one "(" too many
 *   FN_ALU_GetValue_Medium() : forbids too many "("
 *   FN_ALU_GetValue_Strict() : forbids too many "(" and also "undefined"
 */

/*
 * This routine allows for one "(" too many. Needed when parsing indirect
 * addressing modes with internal indices has to be possible. The parameter
 * defines whether "NeedValue" should be called if the value is indefined.
 */
static Value FN_ALU_GetValue_Liberal(int fNeedValue) {
  Value v;
  ffValue = MVALUE_DEFINED;/* sure, defined, not existant */
  VP_Work = FALSE;
  VP_SizeBits = 0;
/* This variable collects "SizeBits". Whenever a value with unknown size is
 * referenced, bit 0 will be set in this variable. Referencing values with
 * 8-bit size sets bit 1, 16-bit-size values set bit 2 and 24-bit-size values
 * set bit 3. So if this variable is still zero after calling the core routine,
 * there wasn't any value parsed at all.*/
  nLevel   = 0;
/* This variable measures the recursion depth, so an error will be produced if
 * the expression uses too many parentheses, for example.*/
  nKlammer = 0;
/* This variable counts the parentheses that are still open. After calling the
 * core routine, it should be zero again (or 1 in special cases, mentioned in
 * the header).*/

  v = FN_ALU_PVR(PRIO_SUBVALUE);
  if(nKlammer > 1) {
    nKlammer = 0;
    FN_Message(pseTooOpen, EERROR);
  }
  if(VP_SizeBits) {
    /* set size info */
    if(VP_SizeBits & SIZEBIT24) {
      /* a 24-bit value was referenced, so mark output as being 24-bit */
      ffValue |= MVALUESIZE_24;
#ifdef FDEBUG
      printf("{Cast 3}");
#endif
    } else {
      /* no 24-bit value was referenced, so check for values of unknown size */
      if((VP_SizeBits & SIZEBITUK) == 0) {
        /* no "unknown size"-values were referenced, so mark as 8- or 16-bit */
        if(VP_SizeBits & SIZEBIT16) {
          /* if any 16-bit value was referenced, mark output as being 16-bit */
          ffValue |= MVALUESIZE_16;
#ifdef FDEBUG
          printf("{Cast 2}");
#endif
        } else {
          /* only 8-bit values were referenced, so mark output as being 8-bit */
          ffValue |= MVALUESIZE_08;
#ifdef FDEBUG
          printf("{Cast 1}");
#endif
        }
      }
    }
  } else {
    /* no sizebits set, so no value could be read */
#ifdef FDEBUG
    printf("{NotGiven}");
#endif
    ffValue |= MVALUE_NONE;/* set "nonexistant" flag */
    ffValue &= ~MVALUE_DEFINED;/* Clear "defined" flag */
  }
  if((ffValue & MVALUE_DEFINED) == 0) {
    v = 0;/* if undefined, return 0. */
    if(fNeedValue) FN_NeedValue();
  }
/*
  printf(" ~VP_ %d _ %h _", Context[nContext].nLines, VP_SizeBits);
  printf(" %h _ %d _", ffValue, v);
*/
  return(v);
}

/*
 * This routine does not tolerate any unmatched "(". If there is no value given
 * at all, an error will be shown.
 */
static Value FN_ALU_GetValue_Medium() {
  Value v = FN_ALU_GetValue_Liberal(TRUE);
  if(nKlammer)              FN_Message(pseSyntax, EERROR);
  if(ffValue & MVALUE_NONE) FN_Message(pseNeedValue, EERROR);
  return(v);
}

/*
 * This routine does not tolerate any unmatched "(" either. In addition to
 * that, it also produces an error if the value is undefined. The error
 * produced is a serious one, so assembly will stop.
 */
static Value FN_ALU_GetValue_Strict() {
  Value v = FN_ALU_GetValue_Liberal(FALSE);
  if(nKlammer) FN_Message(pseSyntax, EERROR);
  if((ffValue & MVALUE_DEFINED) == 0) FN_Message(pseNeedValue, ESERIOUS);
  return(v);
}

/*
 * The actual core of the valueparser, it recursively calls itself when needed.
 * Is re-entrant, of course.
 */
static Value FN_ALU_PVR(int Prio_end) {/* GotByte = first char */
  Value v     = 0,    /* Return value            */
        t     = 0;    /* Temporary value         */
  int   fWork = FALSE,/* Operation on this level */
        fEnd  = FALSE,/* Stop recursion if nothing happens */
        Buf_SizeBits = 0;/* needed when "<", ">" or "^" reset size bits */

#ifdef FDEBUG
  printf("R");
#endif
/* Processing stops when an illegal character is reached or an operator is
 * read whose priority is lower than the given parameter.*/
  nLevel += 1;/* increase recursion counter */
  if(nLevel > MAXLEVEL) FN_Message(pseTooDeep, ESERIOUS);
  hOp_Now = HOP_NONE;/* no operator buffered */
/* First part:
 * Expect operand or unary operator.*/
  SKIPSPACE;
  switch(GotByte) {

    case '!':
    FN_GetByte();
    v = (~FN_ALU_PVR(PRIO_NOT));
    break;

    case '-':
    FN_GetByte();
    v = (-FN_ALU_PVR(PRIO_NEGATE));
    break;

    case '<':
    FN_GetByte();
    Buf_SizeBits = VP_SizeBits;
    v = (FN_ALU_PVR(PRIO_LOWBYTEOF) & 255);
    VP_SizeBits = Buf_SizeBits | SIZEBIT08;
    break;

    case '>':
    FN_GetByte();
    Buf_SizeBits = VP_SizeBits;
    v = ((FN_ALU_PVR(PRIO_HIGHBYTEOF) >> 8) & 255);
    VP_SizeBits = Buf_SizeBits | SIZEBIT08;
    break;

    case '^':
    FN_GetByte();
    Buf_SizeBits = VP_SizeBits;
    v = ((FN_ALU_PVR(PRIO_BANKBYTEOF) >> 16) & 255);
    VP_SizeBits = Buf_SizeBits | SIZEBIT08;
    break;

    case '"':
    /*FALLTHROUGH*/

    case '\'':
    v = FN_ALU_ReadCharValue();
    break;

    case '%':
    v = FN_ALU_ReadBinaryValue();
    break;

    case '$':
    v = FN_ALU_ReadHexadecimalValue();
    break;

    case '&':
    v = FN_ALU_ReadOctalValue();
    break;

    case '*':
    v = FN_ALU_ReadProgramCounter();
    break;

    case '.':
    v = FN_ALU_ReadLocalLabel();
    break;

    case '(':
    v = FN_ALU_ReadSublevelValue();
    break;

    default:/* other characters (of decimal values or global labels) */
#ifdef FDEBUG
    printf("{o}");
#endif
    if((GotByte > 47) && (GotByte < 58)) {
      v = FN_ALU_ReadDecimalValue();
    } else {
      if(pFlagTable[(unsigned char) GotByte] & MBYTEILLEGAL) {
        v = 0;/* illegal character read - so don't go on */
        fEnd = TRUE;
      } else v = FN_ALU_ReadGlobalOrNOT();
    }
  }
/* Second part:
 * Start reading operators and operands in a loop.*/
  while(!fEnd) {
    fEnd = TRUE;/* if nothing happens, stop this recursion level */
    /* if there is no operator present, try to read one */
    if(hOp_Now == HOP_NONE) hOp_Now = FN_ALU_PVR_GetOp();
    if((hOp_Now == HOP_RIGHTPARENTHESIS) && (Prio_end == PRIO_SUBVALUE)) {
      hOp_Now = HOP_NONE;
    }
    if((hOp_Now != HOP_NONE) && (hOp_Now != HOP_RIGHTPARENTHESIS)) {
      /* if there is an operator present now, act upon it */
      fWork = TRUE;
      switch(hOp_Now) {

        case HOP_POWEROF:
        if(PRIO_POWEROF > Prio_end) {
          fEnd = FALSE;
          t = FN_ALU_PVR(PRIO_POWEROF);
          if(t >= 0) {
            v = (Value) pow((double) v, (double) t);
          } else {
            if(ffValue & MVALUE_DEFINED) FN_Message(pseNegExp, EERROR);
            v = 0;
          }
        }
        break;

        case HOP_MULTIPLY:
        if(PRIO_MULTIPLY > Prio_end) {
          fEnd = FALSE;
          v *= FN_ALU_PVR(PRIO_MULTIPLY);
        }
        break;

        case HOP_DIVIDE:
        if(PRIO_DIVIDE > Prio_end) {
          fEnd = FALSE;
          t = FN_ALU_PVR(PRIO_DIVIDE);
          if(t) {
            v /= t;
          } else {
            if(ffValue & MVALUE_DEFINED) FN_Message(pseDivZero, EERROR);
            v = 0;
          }
        }
        break;

        case HOP_MODULO:
        if(PRIO_MODULO > Prio_end) {
          fEnd = FALSE;
          t = FN_ALU_PVR(PRIO_MODULO);
          if(t) {
            v %= t;
          } else {
            if(ffValue & MVALUE_DEFINED) FN_Message(pseDivZero, EERROR);
            v = 0;
          }
        }
        break;

        case HOP_ADD:
        if(PRIO_ADD > Prio_end) {
          fEnd = FALSE;
          v += FN_ALU_PVR(PRIO_ADD);
        }
        break;

        case HOP_SUBTRACT:
        if(PRIO_SUBTRACT > Prio_end) {
          fEnd = FALSE;
          v -= FN_ALU_PVR(PRIO_SUBTRACT);
        }
        break;

        case HOP_SL:
        if(PRIO_SL > Prio_end) {
          fEnd = FALSE;
          v = (v << FN_ALU_PVR(PRIO_SL));
        }
        break;

        case HOP_LSR:
        if(PRIO_LSR > Prio_end) {
          fEnd = FALSE;
          v = (v >> FN_ALU_PVR(PRIO_LSR));
        }
        break;

        case HOP_LE:
        if(PRIO_LE > Prio_end) {
          fEnd = FALSE;
          v = (v <= FN_ALU_PVR(PRIO_LE));
        }
        break;

        case HOP_LT:
        if(PRIO_LT > Prio_end) {
          fEnd = FALSE;
          v = (v < FN_ALU_PVR(PRIO_LT));
        }
        break;

        case HOP_GE:
        if(PRIO_GE > Prio_end) {
          fEnd = FALSE;
          v = (v >= FN_ALU_PVR(PRIO_GE));
        }
        break;

        case HOP_GT:
        if(PRIO_GT > Prio_end) {
          fEnd = FALSE;
          v = (v > FN_ALU_PVR(PRIO_GT));
        }
        break;

        case HOP_NOTEQUAL:
        if(PRIO_NOTEQUAL > Prio_end) {
          fEnd = FALSE;
          v = (v != FN_ALU_PVR(PRIO_NOTEQUAL));
        }
        break;

        case HOP_EQUALS:
        if(PRIO_EQUALS > Prio_end) {
          fEnd = FALSE;
          v = (v == FN_ALU_PVR(PRIO_EQUALS));
        }
        break;

        case HOP_AND:
        if(PRIO_AND > Prio_end) {
          fEnd = FALSE;
          v &= FN_ALU_PVR(PRIO_AND);
        }
        break;

        case HOP_EXOR:
        if(PRIO_EXOR > Prio_end) {
          fEnd = FALSE;
          v ^= FN_ALU_PVR(PRIO_EXOR);
        }
        break;

        case HOP_OR:
        if(PRIO_OR > Prio_end) {
          fEnd = FALSE;
          v |= FN_ALU_PVR(PRIO_OR);
        }
        break;

        default:
        FN_Message(pseUkOp, EERROR);
        fEnd = TRUE;
      }
    }
  }
  nLevel -= 1;/* decrease recursion counter */
  VP_Work = fWork;
#ifdef FDEBUG
  printf("%d_", v);
#endif
  return(v);
}

/*
 * Utility routine for reading an operator
 */
static int FN_ALU_PVR_GetOp() {
  /* no operator buffered, so try to read one */
  SKIPSPACE;
  switch(GotByte) {

    case '^':/* "to the power of" */
    FN_GetByte();
    return(HOP_POWEROF);

    case '*':/* "times" */
    FN_GetByte();
    return(HOP_MULTIPLY);

    case '/':/* "divided by" */
    FN_GetByte();
    return(HOP_DIVIDE);

    case '%':/* "modulo" */
    FN_GetByte();
    return(HOP_MODULO);

    case '+':/* "plus" */
    FN_GetByte();
    return(HOP_ADD);

    case '-':/* "minus" */
    FN_GetByte();
    return(HOP_SUBTRACT);

    case '=':/* "equals" */
    FN_GetByte();
    return(HOP_EQUALS);

    case '&':/* "AND" */
    FN_GetByte();
    return(HOP_AND);

/*
 * This part is commented out because there is no "EXOR" character defined
 *
    case '#':/ * "EXOR" * /
    FN_GetByte();
    return(HOP_EXOR);
*/

    case '|':/* "OR" */
    FN_GetByte();
    return(HOP_OR);

    case '<':/* check for "<", "<=", "<<", "<>" */
    return(FN_ALU_PVR_LT());

    case '>':/* check for ">", ">=", ">>", "><" */
    return(FN_ALU_PVR_HT());

    case '!':/* check for "!=" */
    return(FN_ALU_PVR_EM());

    case ')':/* end of sublevel is handled like an operator*/
    return(FN_ALU_PVR_KZ());

    default:/* check string version of operators */
    if((pFlagTable[(unsigned char) GotByte] & MBYTEILLEGAL) == 0) {
      int len;
      ListItem *p;
      len = FN_Stream_ReadKeyword(&StringItem.String[0], LSMAX, FALSE);
      /* (now: GotByte = illegal character) */
      /* search for list item, length = type + string */
      p = FN_Struct_Search(&StringItem, HTYPE_OPERATOR, len + 1, 0);
      if(p) return(p->Data.Bytes.VALUE);
      FN_Message(pseUkOp, EERROR);
    }
    return(HOP_NONE);
  }
}

/*
 * Routine for reading a subvalue (after left parenthesis)
 */
static Value FN_ALU_ReadSublevelValue() {
  nKlammer++;
  FN_GetByte();/* start sublevel on next char */
  return(FN_ALU_PVR(PRIO_SUBVALUE));
}

/*
 * Routine for finishing a subvalue (after right parenthesis)
 */
static int FN_ALU_PVR_KZ() {
  FN_GetByte();/* proceed with next char */
  if(nKlammer) {
    nKlammer--;
  } else {
    FN_Message(pseTooClosed, EERROR);
  }
  return(HOP_RIGHTPARENTHESIS);
}

/*
 * Routine for reading the program counter
 */
static Value FN_ALU_ReadProgramCounter() {
#ifdef FDEBUG
  printf("{PC=%d}", PC_CPU);
#endif
  FN_GetByte();/* proceed with next char */
  VP_SizeBits |= SIZEBIT16;/* used 16 bit value */
  if(fPCdefined) return(PC_CPU);
  FN_Message(pseNoPC, EERROR);
  return(0);
}

/*
 * Routine for reading a local label
 */
static Value FN_ALU_ReadLocalLabel() {
  int len = FN_Stream_ReadKeyword(&StringItem.String[2], LSMAX - 2, TRUE);

  /* offsets 0 and 1 are for zone */
  /* (now: GotByte = illegal character) */
  if(len) return(FN_ALU_GetLabelValue(Context[nContext].nZone_Now, len));
  return(0);
}

/*
 * Routine for reading a global label (or "NOT")
 */
static Value FN_ALU_ReadGlobalOrNOT() {
  int len = FN_Stream_ReadKeyword(&StringItem.String[2], LSMAX - 2, FALSE);

  /* offsets 0 and 1 are for zone */
  /* (now: GotByte = illegal character) */
  if(len == 3) {
    /* Check for NOT. Okay, it's hardcoded, but so what ? Sue me... */
    if(StringItem.String[2] == 'N') {
      if(StringItem.String[3] == 'O') {
        if(StringItem.String[4] == 'T') {
          return(~FN_ALU_PVR(PRIO_NOT));
        }
      }
    }
  }
  return(FN_ALU_GetLabelValue(NZONE_GLOBAL, len));
}

/*
 * Routine for parsing "<"-operators:  <,  <=,  <<,  <>
 */
static int FN_ALU_PVR_LT() {/* GotByte = "<" */
  switch(FN_GetByte()) {

    case '=':/* "<=", less or equal */
    FN_GetByte();
    return(HOP_LE);

    case '<':/* "<<", shift left */
    FN_GetByte();
    return(HOP_SL);

    case '>':/* "<>", not equal */
    FN_GetByte();
    return(HOP_NOTEQUAL);

    default:/* "<", less than (no bytefetch !) */
    return(HOP_LT);
  }
}

/*
 * Routine for parsing ">"-operators:  >,  >=,  >>,  ><
 */
static int FN_ALU_PVR_HT() {/* GotByte = ">" */
  switch(FN_GetByte()) {

    case '=':/* ">=", more or equal */
    FN_GetByte();
    return(HOP_GE);

    case '<':/* "><", not equal */
    FN_GetByte();
    return(HOP_NOTEQUAL);

    case '>':/* ">>", LSR */
    FN_GetByte();
    return(HOP_LSR);

    default:/* ">", greater than (no bytefetch !) */
    return(HOP_GT);
  }
}

/*
 * Routine for parsing "!="-operator
 */
static int FN_ALU_PVR_EM() {/* GotByte = "!", expecting "=" */
  if(FN_GetByte() == '=') {
    FN_GetByte();
    return(HOP_NOTEQUAL);
  } else {
    FN_Message(pseSyntax, EERROR);
    return(HOP_NONE);
  }
}

/*
 * Routine for parsing quoted characters. The character will be converted using
 * the current conversion table.
 */
static Value FN_ALU_ReadCharValue() {/* GotByte = Quote */
  Value v = (Value) ' ';
  char  c = FN_GetByte();

  if(QuoteChar != NO_QUOTE_CHAR) {
    v = (Value) FN_ConvertChar(c, hCodeTable_Now);
    VP_SizeBits |= SIZEBIT08;/* used 8 bit value */
    FN_GetByte();/* Read closing quote (hopefully) */
    if(QuoteChar == NO_QUOTE_CHAR) {
      FN_GetByte();/* If length == 1, proceed with next byte */
    } else FN_Message(pseTooLong, EERROR);/* If longer than one character */
  } else FN_Message(pseMissingString, EERROR);/* If shorter than one char */
  return(v);/* GotByte = char following closing quote */
}

/*
 * Routine for parsing a binary value. Apart from "0" and "1", it also accepts
 * characters "." and "#", this is much more readable. The current value is
 * returned as soon as a character is read that is none of those given above.
 */
static Value FN_ALU_ReadBinaryValue() {/* GotByte = "%" */
  Value v     =  0;
  int   fCont = TRUE,
        c     = -1;

  do {
    c++;
    switch(FN_GetByte()) {

      case '0':
      case '.':
      v = v << 1;
      break;

      case '1':
      case '#':
      v = (v << 1) + 1;
      break;

      default:
      fCont = FALSE;
    }
  } while(fCont);
  if(c > 8) {
    if(c > 16) VP_SizeBits |= SIZEBIT24;/* used 24 bit value */
    else       VP_SizeBits |= SIZEBIT16;/* used 16 bit value */
  } else       VP_SizeBits |= SIZEBIT08;/* used  8 bit value */
  return(v);/* GotByte = non-binary character */
}

/*
 * Routine for parsing a hexadecimal value. It accepts "0" to "9", "a" to "f"
 * and "A" to "F". Capital letters will be converted to lowercase letters using
 * the flagtable. The current value is returned as soon as a character is read
 * that is none of those given above.
 */
static Value FN_ALU_ReadHexadecimalValue() {/* GotByte = "$" or "x" */
  Value v     = 0;
  char  byte;
  int   fCont,
        c     = -1;

  do {
    c++;
    fCont = FALSE;
    byte = FN_GetByte();
    /*  first, convert "A-F" to "a-f" */
    if(pFlagTable[(unsigned char) byte] & MBYTEUPPERCASE) byte |= 32;
    if((byte < 58) && (byte > 47)) {
      /* add values of digits */
      fCont = TRUE;
      v = (v << 4) + (byte & 15);
    }
    if((byte > 96) && (byte < 103)) {
      /* add values of characters "a-f" */
      fCont = TRUE;
      v = (v << 4) + (byte & 15) + 9;
    }
  } while(fCont);
  if(c > 2) {
    if(c > 4) VP_SizeBits |= SIZEBIT24;/* used 24 bit value */
    else      VP_SizeBits |= SIZEBIT16;/* used 16 bit value */
  } else      VP_SizeBits |= SIZEBIT08;/* used  8 bit value */
  return(v);/* GotByte = non-hexadecimal character */
}

/*
 * Routine for parsing an octal value. It accepts characters "0" to "7". The
 * current value is returned as soon as a character is read that is none of
 * those given above.
 */
static Value FN_ALU_ReadOctalValue() {/* GotByte = "&" */
  Value v = 0;

  FN_GetByte();
  while((GotByte > 47) && (GotByte < 56)) {
    v = (v << 3) + (GotByte & 7);
    FN_GetByte();
  }
  if(v > 255) {
    if(v > 65535) VP_SizeBits |= SIZEBIT24;/* used 24 bit value */
    else          VP_SizeBits |= SIZEBIT16;/* used 16 bit value */
  } else          VP_SizeBits |= SIZEBIT08;/* used  8 bit value */
  return(v);/* GotByte = non-octal character */
}

/*
 * Routine for parsing a decimal value. It accepts characters "0" to "9". The
 * current value is returned as soon as a character is read that is none of
 * those given above. Unlike the others, the "decimal" routine expects the
 * first digit to be read already, because decimal values don't use any prefix.
 * If the first two digits are "0x", this routine branches to the one for
 * parsing hexadecimal values.
 */
static Value FN_ALU_ReadDecimalValue() {/* GotByte = first digit */
  Value v = (GotByte & 15);

  FN_GetByte();
  if((v == 0) && (GotByte == 'x')) return(FN_ALU_ReadHexadecimalValue());
  while((GotByte > 47) && (GotByte < 58)) {
    v *= 10;
    v += (GotByte & 15);
    FN_GetByte();
  }
  if(v > 255) {
    if(v > 65535) VP_SizeBits |= SIZEBIT24;/* used 24 bit value */
    else          VP_SizeBits |= SIZEBIT16;/* used 16 bit value */
  } else          VP_SizeBits |= SIZEBIT08;/* used  8 bit value */
  return(v);/* GotByte = non-decimal character */
}

/*
 * Lookup (and create, if necessary) label list item and return value. The
 * stringbuffer holds the label's name (its length given by "len") and "Zone"
 * its zone (zero for global variables).
 */
static Value FN_ALU_GetLabelValue(Sixteen Zone, int len) {
  ListItem *p;
  int       flags,
            s;

  p = FN_Struct_GetPreparedLabel(Zone, len, MVALUESIZE_UK);
  /* If the label was produced just now, we have to mark it as unsure */
  if(fMadeItem) p->Data.Bytes.FLAGS |= MVALUE_UNSURE;
  flags = p->Data.Bytes.FLAGS;
  ffValue &= (flags | ~MVALUE_DEFINED);
  ffValue |= (flags & MVALUE_UNSURE);
  s = (flags & MVALUE_SIZE);
  VP_SizeBits |= 1 << s;
  return((p->Data.Bytes.LOW)
       + (p->Data.Bytes.HIGH << 8)
       + (p->Data.Bytes.BANK << 16));
}
