//--------------------------------------------------------------
// File     : z80_ub.c
// Datum    : 01.04.2018
// Version  : 1.0
// Autor    : UB
// Web      : http://mikrocontroller.bplaced.net
// CPU      : STM32F746
// IDE      : OpenSTM32
// GCC      : 4.9 2015q2
// Module   : CubeHAL
// Funktion : Z80 emulator (Gameboy version)
//--------------------------------------------------------------

#include "z80_ub.h"
#include "z80_opcode.c"
#include "z80_opcode_cb.c"

static void no_oc(void); // normal opcode
static void cb_oc(void); // cb opcode




//------------------------------------------------
// to avoid if statement after each opcode
// to choose between normal opcode and 0xCB opcodes
// this table is used
//------------------------------------------------
static void (*octype [256])(void)={ // opcodes (0x00 .. 0xFF)
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x00
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x10
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x20
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x30
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x40
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x50
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x60
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x70
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x80
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0x90
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0xa0
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0xb0
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,cb_oc,no_oc,no_oc,no_oc,no_oc, // 0xc0  0xCB !!
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0xd0
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc, // 0xe0
		no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc,no_oc  // 0xf0
};


//--------------------------------------------------------------
// init mcu
// pointer to ROM and lenght of code
//--------------------------------------------------------------
void z80_init(const uint8_t *rom, uint32_t length)
{
	uint32_t n;

	// init z80 struct
	z80.opcode = 0;
	z80.reg.af = 0;
	z80.reg.bc = 0;
	z80.reg.de = 0;
	z80.reg.hl = 0;
	z80.reg.pc = 0;
	z80.reg.sp = 0;
	z80.ime_flag = 0;
	z80.halt_mode = 0;
	z80.halt_skip = 0;
	z80.cycles = 0;
	z80.status = 0;
	z80.rom = rom;

	// clear ram
	for(n=0;n<RAM_SIZE;n++) {
		z80.memory[n+ROM_SIZE]=0x00;
	}

	if(length>ROM_SIZE) length=ROM_SIZE;

	// copy rom into memory
	for(n=0;n<length;n++) {
		z80.memory[n]=*rom;
		rom++;
	}
}

//--------------------------------------------------------------
// mcu reinit
// pointer to ROM and lenght of code
//--------------------------------------------------------------
void z80_reinit(const uint8_t *rom, uint32_t length)
{
	uint32_t n;

	if(length>ROM_SIZE) length=ROM_SIZE;
	z80.rom = rom;

	// copy rom into memory
	for(n=0;n<length;n++) {
		z80.memory[n]=*rom;
		rom++;
	}
}


//--------------------------------------------------------------
// executes single z80 instruction
//--------------------------------------------------------------
void z80_single_step(void)
{
	// read opcode
	z80.opcode = RD_BYTE_MEM(z80.reg.pc);
	// choose between normal and CB opcode
	(*octype[z80.opcode])();
}

//--------------------------------------------------------------
// handle normal opcode
//--------------------------------------------------------------
static void no_oc(void)
{
	// execute normal opcode
	(*opcode0[z80.opcode])();
	// get used mcu cycles
	z80.cycles = cycles[z80.opcode];
}


//--------------------------------------------------------------
// handle CB opcode
//--------------------------------------------------------------
static void cb_oc(void)
{
	// read next opcode
	z80.opcode = RD_BYTE_MEM(z80.reg.pc+1);
	// execute 0xcb opcode
	(*opcode1[z80.opcode])();
	// get used mcu cycles
	z80.cycles = cycles_cb[z80.opcode];
}

//--------------------------------------------------------------
#if SUPPORTED_MBC_VERSION == 1
uint8_t RD_BYTE_MEM(uint16_t adr)
{
	if(adr < ROM_SIZE) {
		return gameboy_rd_from_rom(adr);
	}
	else {
		return z80.memory[adr];
	}
}


void WR_BYTE_MEM(uint16_t adr, uint8_t value)
{
	if(adr < ROM_SIZE) {
		gameboy_wr_into_rom(adr, value);
	}
	else {
		z80.memory[adr] = value;
		if(adr >= MBC0_INTERNAL_REGISTERS) {
			gameboy_wr_internal_register(adr, value);
		}
	}
}

uint16_t RD_WORD_MEM(uint16_t adr)
{
	uint8_t hi, lo;
	uint16_t value;

	lo = RD_BYTE_MEM(adr);
	hi = RD_BYTE_MEM(adr+1);

	value = (hi<<8) | lo;

	return value;
}

void WR_WORD_MEM(uint16_t adr, uint16_t value)
{
	uint8_t u8;

	u8 = (value&0xFF);
	WR_BYTE_MEM(adr, u8);

	u8 = (value>>8);
	WR_BYTE_MEM(adr+1, u8);
}


#endif


