/* Copyright (c) 2013-2016 Jeffrey Pfau
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef LR35902_H
#define LR35902_H

#include "util/common.h"

#include "core/cpu.h"
#include "lr35902/isa-lr35902.h"

struct LR35902Core;

#pragma pack(push, 1)
union FlagRegister {
	struct {
#if defined(__POWERPC__) || defined(__PPC__)
		unsigned z : 1;
		unsigned n : 1;
		unsigned h : 1;
		unsigned c : 1;
		unsigned : 4;
#else
		unsigned : 4;
		unsigned c : 1;
		unsigned h : 1;
		unsigned n : 1;
		unsigned z : 1;
#endif
	};

	uint8_t packed;
};
#pragma pack(pop)

enum LR35902ExecutionState {
	LR35902_CORE_FETCH = 3,
	LR35902_CORE_IDLE_0 = 0,
	LR35902_CORE_IDLE_1 = 1,
	LR35902_CORE_EXECUTE = 2,

	LR35902_CORE_MEMORY_LOAD = 7,
	LR35902_CORE_MEMORY_STORE = 11,
	LR35902_CORE_READ_PC = 15,
	LR35902_CORE_STALL = 19,
	LR35902_CORE_OP2 = 23
};
struct LR35902Memory {
	uint8_t (*cpuLoad8)(struct LR35902Core*, uint16_t address);
	uint8_t (*load8)(struct LR35902Core*, uint16_t address);
	void (*store8)(struct LR35902Core*, uint16_t address, int8_t value);

	uint8_t* activeRegion;
	uint16_t activeMask;
	uint16_t activeRegionEnd;
	void (*setActiveRegion)(struct LR35902Core*, uint16_t address);
};

struct LR35902InterruptHandler {
	void (*reset)(struct LR35902Core* cpu);
	void (*processEvents)(struct LR35902Core* cpu);
	void (*setInterrupts)(struct LR35902Core* cpu, bool enable);
	void (*halt)(struct LR35902Core* cpu);
	void (*stop)(struct LR35902Core* cpu);

	void (*hitIllegal)(struct LR35902Core* cpu);
};

struct LR35902Core {
#pragma pack(push, 1)
	union {
		struct {
			union FlagRegister f;
			uint8_t a;
		};
		uint16_t af;
	};
#pragma pack(pop)
	union {
		struct {
			uint8_t c;
			uint8_t b;
		};
		uint16_t bc;
	};
	union {
		struct {
			uint8_t e;
			uint8_t d;
		};
		uint16_t de;
	};
	union {
		struct {
			uint8_t l;
			uint8_t h;
		};
		uint16_t hl;
	};
	uint16_t sp;
	uint16_t pc;

	uint16_t index;

	int32_t cycles;
	int32_t nextEvent;
	enum LR35902ExecutionState executionState;
	bool halted;

	uint8_t bus;
	bool condition;
	LR35902Instruction instruction;

	bool irqPending;
	uint16_t irqVector;

	struct LR35902Memory memory;
	struct LR35902InterruptHandler irqh;

	struct mCPUComponent* master;

	size_t numComponents;
	struct mCPUComponent** components;
};

static inline uint16_t LR35902ReadHL(struct LR35902Core* cpu) {
	uint16_t hl;
	LOAD_16LE(hl, 0, &cpu->hl);
	return hl;
}

static inline void LR35902WriteHL(struct LR35902Core* cpu, uint16_t hl) {
	STORE_16LE(hl, 0, &cpu->hl);
}

static inline uint16_t LR35902ReadBC(struct LR35902Core* cpu) {
	uint16_t bc;
	LOAD_16LE(bc, 0, &cpu->bc);
	return bc;
}

static inline void LR35902WriteBC(struct LR35902Core* cpu, uint16_t bc) {
	STORE_16LE(bc, 0, &cpu->bc);
}

static inline uint16_t LR35902ReadDE(struct LR35902Core* cpu) {
	uint16_t de;
	LOAD_16LE(de, 0, &cpu->de);
	return de;
}

static inline void LR35902WriteDE(struct LR35902Core* cpu, uint16_t de) {
	STORE_16LE(de, 0, &cpu->de);
}

void LR35902Init(struct LR35902Core* cpu);
void LR35902Deinit(struct LR35902Core* cpu);
void LR35902SetComponents(struct LR35902Core* cpu, struct mCPUComponent* master, int extra, struct mCPUComponent** extras);
void LR35902HotplugAttach(struct LR35902Core* cpu, size_t slot);
void LR35902HotplugDetach(struct LR35902Core* cpu, size_t slot);

void LR35902Reset(struct LR35902Core* cpu);
void LR35902RaiseIRQ(struct LR35902Core* cpu, uint8_t vector);

void LR35902Tick(struct LR35902Core* cpu);
void LR35902Run(struct LR35902Core* cpu);

#endif