enum {
  A, F, AF,
  B, C, BC,
  D, E, DE,
  H, L, HL,
  SP, PC,
};

enum {
  ZF, NF, HF, CF,
};

//register base class
//the idea here is to have all registers derive from a single base class.
//this allows construction of opcodes that can take any register as input or output,
//despite the fact that behind-the-scenes, special handling is done for eg: F, AF, HL, etc.
//registers can also be chained together: eg af = 0x0000 writes both a and f.
struct Register {
  virtual operator unsigned() const = 0;
  virtual unsigned operator=(unsigned x) = 0;
  Register& operator=(const Register& x) { operator=((unsigned)x); return *this; }

  unsigned operator++(int) { unsigned r = *this; operator=(*this + 1); return r; }
  unsigned operator--(int) { unsigned r = *this; operator=(*this - 1); return r; }
  unsigned operator++() { return operator=(*this + 1); }
  unsigned operator--() { return operator=(*this - 1); }

  unsigned operator |=(unsigned x) { return operator=(*this | x); }
  unsigned operator ^=(unsigned x) { return operator=(*this ^ x); }
  unsigned operator &=(unsigned x) { return operator=(*this & x); }

  unsigned operator<<=(unsigned x) { return operator=(*this << x); }
  unsigned operator>>=(unsigned x) { return operator=(*this >> x); }

  unsigned operator +=(unsigned x) { return operator=(*this + x); }
  unsigned operator -=(unsigned x) { return operator=(*this - x); }
  unsigned operator *=(unsigned x) { return operator=(*this * x); }
  unsigned operator /=(unsigned x) { return operator=(*this / x); }
  unsigned operator %=(unsigned x) { return operator=(*this % x); }
};

struct Register8 : Register {
  uint8 data;
  operator unsigned() const { return data; }
  unsigned operator=(unsigned x) { return data = x; }
};

struct RegisterF : Register {
  bool z, n, h, c;
  operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); }
  unsigned operator=(unsigned x) { z = x & 0x80; n = x & 0x40; h = x & 0x20; c = x & 0x10; return *this; }
  bool& operator[](unsigned r) {
    static bool* table[] = {&z, &n, &h, &c};
    return *table[r];
  }
};

struct Register16 : Register {
  uint16 data;
  operator unsigned() const { return data; }
  unsigned operator=(unsigned x) { return data = x; }
};

struct RegisterAF : Register {
  Register8& hi;
  RegisterF& lo;
  operator unsigned() const { return (hi << 8) | (lo << 0); }
  unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
  RegisterAF(Register8& hi, RegisterF& lo) : hi(hi), lo(lo) {}
};

struct RegisterW : Register {
  Register8& hi;
  Register8& lo;
  operator unsigned() const { return (hi << 8) | (lo << 0); }
  unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; }
  RegisterW(Register8& hi, Register8& lo) : hi(hi), lo(lo) {}
};

struct Registers {
  Register8  a;
  RegisterF  f;
  RegisterAF af;
  Register8  b;
  Register8  c;
  RegisterW  bc;
  Register8  d;
  Register8  e;
  RegisterW  de;
  Register8  h;
  Register8  l;
  RegisterW  hl;
  Register16 sp;
  Register16 pc;

  bool halt;
  bool stop;
  bool ei;
  bool ime;

  Register& operator[](unsigned r) {
    static Register* table[] = {&a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc};
    return *table[r];
  }

  Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {}
} r;
