//--------------------------------------------------------------
// File     : z80_ub.h
//--------------------------------------------------------------

#ifndef Z80_UB_H_
#define Z80_UB_H_


#include "stm32f7xx.h"
#include "gameboy_ub.h"


// struct for all mcu register [a,f,b,c,d,e,h,l / PC,SP]
// two 8bit registers combined to a 16bit registerpair
typedef struct {
	struct {
		union {
			struct {
				uint8_t f; // lo	8bit flag register 'F'
				uint8_t a; // hi	8bit register 'A'
			};
			uint16_t af;	//		combined 16bit registerpair 'AF'
		};
	};

	struct {
		union {
			struct {
				uint8_t c; // lo	8bit register 'C'
				uint8_t b; // hi	8bit register 'B'
			};
			uint16_t bc;	//		combined 16bit registerpair 'BC'
		};
	};

	struct {
		union {
			struct {
				uint8_t e; // lo	8bit register 'E'
				uint8_t d; // hi	8bit register 'D'
			};
			uint16_t de;	//		combined 16bit registerpair 'DE'
		};
	};

	struct {
		union {
			struct {
				uint8_t l; // lo	8bit register 'L'
				uint8_t h; // hi	8bit register 'H'
			};
			uint16_t hl;	//		combined 16bit registerpair 'HL'
		};
	};

	struct {
		union {
			struct {
				uint8_t pc_lo; // lo	lo byte of 16bit register 'PC'
				uint8_t pc_hi; // hi	hi byte of 16bit register 'PC'
			};
			uint16_t pc;	//		combined 16bit registerpair 'PC'
		};
	};

	struct {
		union {
			struct {
				uint8_t sp_lo; // lo	lo byte of 16bit register 'PC'
				uint8_t sp_hi; // hi	hi byte of 16bit register 'PC'
			};
			uint16_t sp;	//		combined 16bit registerpair 'SP'
		};
	};
}z80reg;


// dont change these flag values !! (used in all tables)
#define FLAG_Z	0x80	// zero flag : '1' if value = 0, '0' if value != 0
#define FLAG_N	0x40	// subtract flag : '1' if command SUB, '0' if command not SUB
#define FLAG_H	0x20	// half carry : '1' if overflow from bit3
#define FLAG_C	0x10	// carry flag : '1' if overflow from bit7
#define FLAG_3	0x08	// not used in gameboy
#define FLAG_2	0x04	// not used in gameboy
#define FLAG_1	0x02	// not used in gameboy
#define FLAG_0	0x01	// not used in gameboy
#define FLAG_MASK	0xF0	// to mask flags z,n,h,c

// dont change these flag values !!
#define BIT3_MASK8		0x08	// bit3 = hi (8bit value)
#define BIT11_MASK16	0x0800	// bit11 = hi (16bit value)

// dont change these values !!
#define ROM_SIZE	0x8000	// 32k ROM
#define RAM_SIZE	0x8000	// 32k RAM
#define MEM_SIZE	(ROM_SIZE+RAM_SIZE)	// 64k memory

// status flags
#define STATUS_ERR_FLAG		0x01	// set on error
#define STATUS_STOP_FLAG	0x02	// set on STOP opcode



// struct of z80
typedef struct {
	uint8_t opcode;				// current opcode
	z80reg reg;					// register values
	uint8_t ime_flag; 			// 0=global interrupt disable, 1=global interrupt enable
	uint8_t halt_mode;	 		// 0=first call, 1=wait
	uint8_t halt_skip;	 		// 1=skip halt opcode
	uint8_t cycles;				// current mcu cylces
	uint8_t memory[MEM_SIZE];	// memory (ROM+RAM)
	const uint8_t *rom;			// pointer to the rom start adr
	uint8_t status;				// status flags
}z80_t;
z80_t z80;


// temp variables
uint8_t z80_byte;	// 8bit unsigned variable
uint16_t z80_word;	// 16bit unsigned variable
uint32_t z80_long;	// 32bit unsigned variable
uint8_t u8_index;	// 8bit unsigned
uint8_t u8_temp;	// 8bit unsigned

// 8bit combined unsigned/signed variable
union
{
	uint8_t uint8;
	int8_t sint8;
}var8bit;

//--------------------------------------------------------------
// memory interactions (8bit)
// select supported memory controller in "gameboy_ub.h"
//--------------------------------------------------------------


#if SUPPORTED_MBC_VERSION == 0

// read 8bit from memory
#define RD_BYTE_MEM(adr) (z80.memory[adr])

// write 8bit into memory
#define WR_BYTE_MEM(adr, value) { \
if(adr >= ROM_SIZE) { \
z80.memory[adr] = value; \
} \
if(adr >= MBC0_INTERNAL_REGISTERS) { \
	gameboy_wr_internal_register(adr, value); \
} \
}

#endif

#if SUPPORTED_MBC_VERSION == 1
uint8_t RD_BYTE_MEM(uint16_t adr);
void WR_BYTE_MEM(uint16_t adr, uint8_t value);
#endif



//--------------------------------------------------------------
// memory interactions (16bit)
//--------------------------------------------------------------

#if SUPPORTED_MBC_VERSION == 0

// read 16bit from memory
#define RD_WORD_MEM(adr) ((z80.memory[adr+1]<<8) | (z80.memory[adr]))

// write 16bit into memory
#define WR_WORD_MEM(adr, value) {z80.memory[adr] = (value&0xFF);z80.memory[adr+1] = (value>>8);}

#endif


#if SUPPORTED_MBC_VERSION == 1
uint16_t RD_WORD_MEM(uint16_t adr);
void WR_WORD_MEM(uint16_t adr, uint16_t value);
#endif

//--------------------------------------------------------------
#define OPCODE_ERR (z80.status |= STATUS_ERR_FLAG)



//--------------------------------------------------------------
void z80_init(const uint8_t *rom, uint32_t length);
void z80_reinit(const uint8_t *rom, uint32_t length);
void z80_single_step(void);



#endif /* Z80_UB_H_ */
