/* processor.h
 */

#ifndef _processor_h
#define _processor_h

#include "hc08monitor.h"
#include "hc08ioport.h"

#define IMGSTART    0x8000
#define IMGLEN      0x8000
#define IMGEND      (IMGSTART+IMGLEN)
#define PAGELEN   32
#define FLGLEN      (IMGLEN/PAGELEN)

#define PROTVECTOR    0xfff6
#define PROTCVECTORLEN  10

#define RUN_STEP_INTO -1
#define RUN_STEP_OVER -2
#define RUN_TO_BREAK  -3

#define NOBREAKPOINT  -1

typedef enum { DIR_REL, IMM_REL, DIR8, REL, INH, IMM, IMM16, EXT, JEXT, IX, IX1, IX2, SP1, SP1_REL,
  SP2, DIX_P, IX_P_REL, IX1_P_REL, IX_P_D, DD, IMD} admod;

typedef struct op {
    const char* op_name;
    int op_len;
    admod op_mode;
    int cycles;
  } op;

#define   NUMFPPOINTS 11 /* (sizeof (prrtAddrs) / sizeof (int)) */

typedef struct progRoutines {
  const unsigned char *pgm;
  int pgmStart, pgmEnd, pgmLen;   // Routine di programmazione
  const unsigned char *era;
  int eraStart, eraEnd, eraLen;   // Dati routine di cancellazione (MASS Erase)
  int ramstart;                   // Inizio RAM per controllo cacellazione
  int pagelen;                    // Dimensione blocco dati da progammare
  int staddressC;                 // Indirizzo da indicare per il "MASS ERASE" su un dispositivo cancellato
  int staddressP;                 // Indirizzo da indicare per il "MASS ERASE" su un dispositivo programmato
  const char *descr;                    // Striga di descrizione del processore riconosciuto
} progRoutines;

class Processor : public Monitor {
private :
  // CPU Registers
  int oldPc;
  int pc;
  int sp;
  int oldHx;
  int hx;
  unsigned char oldA;
  unsigned char a;
  unsigned char oldCcr;
  unsigned char ccr;

  int firstOp, secondOp;

  unsigned char *memory;
  IoPort * ports;
  bool emulating;

  int bpAddress;    // Standard breakpoint (only one accepted)
  int tmpBpAddress; // Temporary breakpoint, for "run to"; overrides standard one

  char disass [20]; // Disassembly buffer

  static unsigned char clean [];

  static progRoutines procConfigs [];
  static int prrtAddrs [];  // Indrizzi da verificare per il fingerprint
  static unsigned char prints [][NUMFPPOINTS];
  int curProcessor;   // Processore corrente (riconosciuto)

  bool procRunning;

  int fingerPrint (void);

  int programS19 (const char *hexfile);
  void uploadPrg (const unsigned char *pgm, int start, int num); /* private */
  void sendWrCommand (int stad, int datalen, unsigned char *data, int pagelen); /* private */

  int hex (char *c);
  int hex0 (char c);

  int nextBp (int mode, int opcode, int opcode2, op *curop);

  unsigned int getAddr (admod modo);
  int signExtend8 (unsigned int n);
  unsigned int rel ();
  void push (unsigned char d);
  unsigned char pull ();

  void noop ();
  void adc (admod am);
  void add (admod am);
  void andl (admod am);
  unsigned char asl (unsigned char m);
  unsigned char asr (unsigned char m);
  void bitl (admod am);
  void cmp (admod am);
  unsigned char cmp (unsigned char a);
  unsigned char com (unsigned char m);
  void comm (admod am);
  void cphx (admod am);
  void cpx (admod am);
  void daa ();
  int dnz (admod am);
  unsigned char dec (unsigned char m);
  void decm (admod am);
  void div ();
  void eor (admod am);
  unsigned char inc (unsigned char m);
  void incm (admod am);
  void jsr (admod am);
  void lda (admod am);
  void ldhx (admod am);
  void ldx (admod am);
  unsigned char lsr (unsigned char m);
  void lsrm (admod am);
  void mul ();
  unsigned char neg (unsigned char m);
  void negm (admod am);
  void ora (admod am);
  unsigned char rol (unsigned char m);
  void rolm (admod am);
  unsigned char ror (unsigned char m);
  void rorm (admod am);
  void sbc (admod am);
  void sta (admod am);
  void sthx (admod am);
  void stx (admod am);
  void stop ();
  void sub (admod am);
  void tst (unsigned char m);
  void tstm (admod am);
  void wait ();

  void emulate9E ();

public:
  Processor (Printer *p) : Monitor (p) { init (); ports = (IoPort*)0; };
  void init () { oldPc=oldHx=-1; sp=0xff; pc=hx = 0; oldA=0; a=0xff; oldCcr=0; ccr=0x68;
    emulating = false; procRunning = false; bpAddress = NOBREAKPOINT; tmpBpAddress = NOBREAKPOINT; };
	
  int getPc () { return pc; };
  int getSp () { return sp; };
  int getHx () { return hx; };
  int getA () { return a; };
  int getCcr () { return ccr; };
  unsigned char getMem (unsigned int i);
  void setMem(unsigned int i, unsigned char d);
  void setPc (int PC) { pc = PC; };
  void setHx (int HX) { hx = HX; };
  void setA (int A) { a = A & 0xff; };
  void setCcr (int CCR) { ccr = CCR & 0xff; };

  int getFirstOp () { return firstOp; };
  int getSecondOp () { return secondOp; };

  void emulateProgram (const char *hexfile);
  void reset ();
  void emulate ();
  int read (int Addr);              // Sostituisce quella di hc08monitor, per gestire emulazione
  int readMem (int Addr);           // Sostituisce quella di hc08monitor, per gestire emulazione
  void write (int Addr, int Dato);  // Sostituisce quella di hc08monitor, per gestire emulazione

  void addPort (IoPort *p) { p->setNext (ports); ports = p; };

  const unsigned char *pgmCode () { return procConfigs [curProcessor].pgm; };
  int pgmCodeStart () { return procConfigs [curProcessor].pgmStart; };
  int pgmCodeLen () { return procConfigs [curProcessor].pgmLen; };
  const unsigned char *eraCode () { return procConfigs [curProcessor].era; };
  int eraCodeStart () { return procConfigs [curProcessor].eraStart; };
  int eraCodeLen () { return procConfigs [curProcessor].eraLen; };
  int eraAddress (int clean) { return (clean) ?
    procConfigs [curProcessor].staddressC : procConfigs [curProcessor].staddressP; };
  int pageLen () { return procConfigs [curProcessor].pagelen; };

  void setupCom (const char *comname) { Com::setupCom (comname); init (); };
  void stackprep ();
  void readRegisters ();
  char *step (int Bp);
  bool runEnded ();
  void endStep ();
  void go (int start);
  int resetVector (unsigned char *vector);
  int disassembly (int PC, char *buffer);

  void readS19 (const char *hexfile, int start, int imglen,unsigned char *prog, unsigned char *used);
  int isErased (unsigned char *resVector);
  void programChip (const char *hexfile);
  int verifyChip (const char *hexfile);
  void clearChip ();
  bool isRunning () { return procRunning; };
  bool hasBp () { return bpAddress != NOBREAKPOINT; };
  bool hasBp (int refBp) { return bpAddress != NOBREAKPOINT && bpAddress != refBp; };
  void setBp (int where) { bpAddress = where; };
  void setTempBp (int where) { tmpBpAddress = where; };
};

#endif
