// Z80m.h: interface for the CZ80m class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_Z80M_H__1FE4841B_50C5_45E5_8B01_B5E9AC0C7474__INCLUDED_)
#define AFX_Z80M_H__1FE4841B_50C5_45E5_8B01_B5E9AC0C7474__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <stdlib.h>
#include "platform.h"
#include "Memory.h"
#include "interrupt.h"


#define C_FLAG		(1<<4)	// Carry flag
#define H_FLAG		(1<<5)	// Half Carry flag
#define N_FLAG		(1<<6)	// Subtract flag
#define Z_FLAG      (1<<7)	// Zero flag



class CGBMachine;

//the CPU which GameBoy used, I call it Z80m, mean Z80 modified!

typedef struct _tag_Z80m_8BIT_ 
{
	u8 F, A; //F can't be accesed by CPU in a normal way
	u8 dummy1[2];
	u8 C, B;
	u8 dummy2[2];
	u8 E, D;
	u8 dummy3[2];
	u8 L, H;
	u8 dummy4[10];
} _Z80m_8BIT_;

typedef struct _tag_Z80m_16BIT_ 
{
	u32 AF;
	u32 BC;
	u32 DE;
	u32 HL;
	u32 SP; //Stack Pointer
	u32 PC; //Program Counter
} _Z80m_16BIT_;

typedef struct _tag_Z80m_FLAGS_
{
	u8   Unused	:4;
	bool C		:1;
	bool H		:1;
	bool S		:1;
	bool Z		:1;
	u8 dummy[21];
} _Z80m_FLAGS_;

typedef union _tag_GB_CPU_
{
	_Z80m_8BIT_  Reg8;
	_Z80m_16BIT_ Reg16;
	_Z80m_FLAGS_ Flags;
} _GB_CPU_;

class CInterrupt;
class CMemory;
class CInput;

class CZ80m
{
	friend class CInterrupt;
	friend class CMemory;
public:
	CZ80m(): m_Reg16(*((_Z80m_16BIT_*)&m_Reg8)),m_Flags(*((_Z80m_FLAGS_*)&m_Reg8))
	{
		m_nDoubleSpeed = 0;
		m_nClocks = 0;
		m_pMemory = NULL;
		m_pMachine = NULL;
		m_pInterrupt = NULL;
		m_pInput = NULL;

		m_nDivClks = 0;
		m_nTimerClks = 0;

		interrupts_enable_count = 0;

		InitCPU();
	}
	virtual ~CZ80m();
public:
	void SetMemory(CMemory* pMemory);
	void SetMachine(CGBMachine* pMachine);
	void SetInterrupt(CInterrupt* pInterrupt);
	inline void SetInput(CInput* pInput)
	{
		m_pInput = pInput;
	}

	void InitCPU();
	void ExecStep(void);
	void AddClock(int clks);

public:
	_Z80m_8BIT_	  m_Reg8;
	_Z80m_16BIT_& m_Reg16;
	_Z80m_FLAGS_& m_Flags;

	u32			m_nDoubleSpeed;
	int			m_nClocks;
	CMemory*	m_pMemory;
	CGBMachine*	m_pMachine;
	CInterrupt*	m_pInterrupt;
	CInput*		m_pInput;
	int			m_nDivClks;
	int			m_nTimerClks;

	int			interrupts_enable_count;
};

inline void CZ80m::AddClock(int clks)
{
	m_nClocks += clks;
	m_nDivClks += clks;
	if(m_pMemory->m_IOPorts[TAC_REG-0xFF00] & 0x04) // Timer is enable
		m_nTimerClks += clks;


	if(m_pInterrupt->m_nVBlankDelayClks)
	{
		m_pInterrupt->m_nVBlankDelayClks -= clks;

		if(m_pInterrupt->m_nVBlankDelayClks <= 0)
		{
			m_pInterrupt->SetInterrupt(I_VBLANK);
			m_pInterrupt->m_nVBlankDelayClks = 0;
		}
	}
}


#endif // !defined(AFX_Z80M_H__1FE4841B_50C5_45E5_8B01_B5E9AC0C7474__INCLUDED_)
