#ifndef _CPU_H
#define _CPU_H

#define SETFLAGS_ZN(VALUE) ST = (ST&0x7D)|(((VALUE)==0)<<1)|((VALUE)&0x80)

#include "mem.h"

class TED;

class CPU {
	protected:
		//unsigned int currins;
		unsigned char currins;
		unsigned char nextins;
		unsigned char farins;
		unsigned int  ptr;
		unsigned int  PC;
		unsigned char  SP;
						   // 76543210
		unsigned int  ST; // NV1BDIZC
		unsigned char  AC;
		unsigned char  X;
		unsigned char  Y;
		void ADC(const unsigned char value);
		void SBC(const unsigned char value);
		void DoCompare(unsigned char reg, unsigned char value);
		unsigned int cycle;
		unsigned int IRQcount;
		unsigned char *irq_register;
		unsigned char *stack;
		unsigned char irq_sequence;
		MemoryHandler *mem;
		virtual unsigned char CheckVFlag() { return (ST&0x40); };
		inline virtual void ClearVFlag() { ST&=0xBF; };
		inline void SetVFlag() { ST|=0x40; };

	public:
		CPU(MemoryHandler *memhandler, unsigned char *irqreg, unsigned char *cpustack);
		virtual ~CPU();
		void Reset(void);
		void softreset(void);
		void setPC(unsigned int addr);
		void setST(unsigned char st) { ST = st; };
		void process();
		void stopcycle();
		
		unsigned int getPC() { return PC; };
		unsigned int getSP() { return SP; };
		unsigned int getST() { return ST; };
		unsigned int getAC() { return AC; };
		unsigned int getX() { return X; };
		unsigned int getY() { return Y; };
		unsigned int getnextins() { return nextins; };
		unsigned int getptr() { return ptr; };
		unsigned int getcycle() { return cycle; };
		unsigned int getcins();

		bool saveshot(void *cpudump);
		bool loadshot(void *cpudump);
		// breakpoint variables
		static bool bp_active;
		static bool bp_reached;
		struct {
  			unsigned int address;
  			bool enabled;
  			bool slot_free;  			
		} bp[11];
		unsigned int nr_activebps;
		bool cpu_jammed;
		static const unsigned int nr_of_bps;	
};

inline void CPU::ADC(const unsigned char value)
{
	if (ST&0x08) {
		unsigned int bin_adc = AC + value + (ST&0x01);
		unsigned char AL, AH;

		AL=(AC&0x0F) + (value & 0x0F) + (ST&0x01);
		AH=(AC >> 4) + (value >> 4 ) ;
		// fix lower nybble
		if (AL>9) {
			AL+=6;
			AH++;
		}
		// zero bit depends on the normal ADC...
		(bin_adc)&0xFF ? ST&=0xFD : ST|=0x02;
		// negative flag also...
		( AH & 0x08 ) ? ST|=0x80 : ST&=0x7F; 
		// V flag
		((((AH << 4) ^ AC) & 0x80) && !((AC ^ value) & 0x80)) ? SetVFlag() : ClearVFlag();
		// fix upper nybble
		if (AH>9) 
			AH+=6;
		// set the Carry if the decimal add has an overflow
		(AH > 0x0f) ? ST|=0x01 : ST&=0xFE;
		// calculate new AC value
		AC = (AH<<4)|(AL & 0x0F);
	} else {
		unsigned int bin_adc = AC + value + (ST&0x01);
		(bin_adc>=0x100) ? ST|=0x01 : ST&=0xFE;
		(!((AC ^ value) & 0x80) && ((AC ^ bin_adc) & 0x80) ) ? SetVFlag() : ClearVFlag();
		AC=(unsigned char) bin_adc;
		SETFLAGS_ZN(AC);
	}
}

inline void CPU::SBC(const unsigned char value)
{
	if (ST&0x08) { // if in decimal mode
		unsigned int bin_sbc = (AC - value - (1-(ST&0x01)));
		unsigned int dec_sbc = (AC & 0x0F) - (value & 0x0F) - (1-(ST&0x01));
		// Calculate the upper nybble.
		// fix upper nybble
		if (dec_sbc&0x10)
			dec_sbc = ((dec_sbc-6)&0x0F) | ((AC&0xF0) - (value&0xF0) - 0x10);
		else
			dec_sbc = (dec_sbc&0x0F) | ((AC&0xF0) - (value&0xF0));
		
		if (dec_sbc&0x100)
			dec_sbc-= 0x60;

		// all flags depend on the normal SBC...
		(bin_sbc<0x100) ? ST|=0x01 : ST&=0xFE ; // carry flag
		SETFLAGS_ZN( bin_sbc & 0xFF );
		((AC^bin_sbc)&0x80 && (AC^value)&0x80 ) ? SetVFlag() : ClearVFlag(); // V flag

		AC=(unsigned char) dec_sbc;

	} else {
		unsigned int bin_sbc = (AC - value - (1-(ST&0x01)));
		(bin_sbc<0x100) ? ST|=0x01 : ST&=0xFE;
		(((AC ^ value) & 0x80) && ((AC ^ bin_sbc) & 0x80) ) ? SetVFlag() : ClearVFlag();
		AC=(unsigned char) bin_sbc;
		SETFLAGS_ZN(AC);
	}
}



#endif // _CPU_H
