class CPU : public MMIO {
public:
  virtual void enter() = 0;

  //CPU version number
  //* 1 and 2 are known
  //* reported by $4210
  //* affects timing (DRAM refresh, HDMA init, etc)
  uint8 cpu_version;

  virtual uint8 pio() = 0;
  virtual bool joylatch() = 0;
  virtual uint8 port_read(uint8 port) = 0;
  virtual void port_write(uint8 port, uint8 value) = 0;

  #include "cpuregs.hpp"
  regs_t regs;

  virtual void scanline() = 0;
  virtual void power();
  virtual void reset();

  /*****
   * in opcode-based CPU emulators, the main emulation routine
   * will only be able to call the disassemble_opcode() function
   * on clean opcode edges. but with cycle-based CPU emulators,
   * the CPU may be in the middle of executing an opcode when the
   * emulator (e.g. debugger) wants to disassemble an opcode. this
   * would mean that important registers may not reflect what they
   * did at the start of the opcode (especially regs.pc), so in
   * cycle-based emulators, this function should be overridden to
   * reflect whether or not an opcode has only been partially
   * executed. if not, the debugger should abort attempts to skip,
   * disable, or disassemble the current opcode.
   *****/
  virtual bool in_opcode() { return false; }

  /*****
   * opcode disassembler
   *****/
  enum {
    OPTYPE_DP = 0,    //dp
    OPTYPE_DPX,       //dp,x
    OPTYPE_DPY,       //dp,y
    OPTYPE_IDP,       //(dp)
    OPTYPE_IDPX,      //(dp,x)
    OPTYPE_IDPY,      //(dp),y
    OPTYPE_ILDP,      //[dp]
    OPTYPE_ILDPY,     //[dp],y
    OPTYPE_ADDR,      //addr
    OPTYPE_ADDRX,     //addr,x
    OPTYPE_ADDRY,     //addr,y
    OPTYPE_IADDRX,    //(addr,x)
    OPTYPE_ILADDR,    //[addr]
    OPTYPE_LONG,      //long
    OPTYPE_LONGX,     //long, x
    OPTYPE_SR,        //sr,s
    OPTYPE_ISRY,      //(sr,s),y
    OPTYPE_ADDR_PC,   //pbr:addr
    OPTYPE_IADDR_PC,  //pbr:(addr)
    OPTYPE_RELB,      //relb
    OPTYPE_RELW,      //relw
  };

  void   disassemble_opcode(char *output);
  uint8  dreadb(uint32 addr);
  uint16 dreadw(uint32 addr);
  uint32 dreadl(uint32 addr);
  uint32 decode(uint8 offset_type, uint32 addr);
  uint8  opcode_length();

  CPU();
  virtual ~CPU();
};
