#ifndef SH2_H_
#define SH2_H_
#include <stdio.h>
#include "backend.h"
#include "tern.h"

typedef struct sh2_options sh2_options;

typedef struct sh2_context sh2_context;
typedef void (sh2_fun)(sh2_context *sh2);
void init_sh2_opts(sh2_options *opts, const memmap_chunk *chunks, uint32_t num_chunks);
sh2_context *init_sh2_context(sh2_options *opts, sh2_fun *next_int);
void sh2_free(sh2_context *sh2);
void sh2_assert_reset(sh2_context *sh2);
void sh2_clear_reset(sh2_context *sh2);
void sh2_set_cache_enabled(sh2_context *sh2, uint8_t enabled);
void sh2_run(sh2_context *sh2, uint32_t target_cycle);
void sh2_insert_breakpoint(sh2_context *sh2, uint32_t address, sh2_fun *handler);
void sh2_remove_breakpoint(sh2_context *sh2, uint32_t address);
typedef void (sh2_write32)(uint32_t address, sh2_context *sh2, uint32_t value);
typedef void (sh2_write16)(uint32_t address, sh2_context *sh2, uint16_t value);
typedef void (sh2_write8)(uint32_t address, sh2_context *sh2, uint8_t value);
typedef uint32_t (sh2_read32)(uint32_t address, sh2_context *sh2);
typedef uint16_t (sh2_read16)(uint32_t address, sh2_context *sh2);
typedef uint8_t (sh2_read8)(uint32_t address, sh2_context *sh2);
typedef void (sh2_burst_read)(uint32_t address, sh2_context *sh2, uint32_t *dst);

struct sh2_options {
	cpu_options gen;
	FILE* address_log;
};

struct sh2_context {
	sh2_options *opts;
	tern_node *breakpoints;
	void *system;
	void *periph_state;
	sh2_read32 *read32[8];
	sh2_read16 *read16[8];
	sh2_read8 *read8[8];
	sh2_write32 *write32[8];
	sh2_write16 *write16[8];
	sh2_write8 *write8[8];
	sh2_fun *periph_reset;
	sh2_fun *periph_run;
	sh2_fun *int_ack;
	sh2_fun *calc_next_interrupt;
	sh2_burst_read *burst_read;
	uint8_t *mem_pointers[4];
	uint32_t cache[1024];
	uint32_t cache_address[256];
	uint32_t cache_lru[64];
	uint32_t gpr[16];
	uint32_t vbr;
	uint32_t sync_cycle;
	uint32_t sr;
	uint32_t scratch2;
	uint32_t scratch1;
	uint32_t pr;
	uint32_t pc;
	uint32_t macl;
	uint32_t mach;
	uint32_t int_vector;
	uint32_t int_cycle;
	uint32_t gbr;
	uint32_t cycles;
	uint32_t current_way_off;
	uint16_t prefetch_next;
	uint16_t prefetch_cur;
	uint8_t peripherals[512];
	uint8_t t;
	uint8_t sleeping;
	uint8_t s;
	uint8_t reset;
	uint8_t q;
	uint8_t prefetch_full;
	uint8_t need_reset;
	uint8_t main;
	uint8_t m;
	uint8_t int_priority;
	uint8_t did_mem;
	uint8_t delay_slot;
	uint8_t cache_tw;
	uint8_t cache_od;
	uint8_t cache_id;
	uint8_t cache_enabled;
};

void sh2_execute(sh2_context *context, uint32_t target_cycle);
void sh2_reset(sh2_context *context);
void sh2_disperse_sr(sh2_context *context);
void sh2_collect_sr(sh2_context *context);
#endif //SH2_H_
