/******************************************************************************

	m68000.c

	M68000 CPUインタフェース関数

******************************************************************************/

#include "emumain.h"

#if (EMU_SYSTEM == CPS2)
static u32 m68k_encrypt_start;
static u32 m68k_encrypt_end;
static u8  *m68k_decrypted_rom;

void m68000_set_encrypted_range(u32 start, u32 end, void *decrypted_rom)
{
	m68k_encrypt_start = start;
	m68k_encrypt_end   = end;
	m68k_decrypted_rom = (u8 *)decrypted_rom;
}

static u8 m68000_read_pcrelative_8(u32 offset)
{
	if (offset >= m68k_encrypt_start && offset <= m68k_encrypt_end)
		return m68k_decrypted_rom[offset ^ 1];
	else
		return m68000_read_memory_8(offset);
}

static u16 m68000_read_pcrelative_16(u32 offset)
{
	if (offset >= m68k_encrypt_start && offset <= m68k_encrypt_end)
		return *(u16 *)&m68k_decrypted_rom[offset];
	else
		return m68000_read_memory_16(offset);
}
#endif

static int irq_state;

static int m68000_irq_callback(int irqline)
{
	if (irq_state == HOLD_LINE)
	{
		irq_state = CLEAR_LINE;
		C68k_Set_IRQ(&C68K, 0);
	}
	return C68K_INTERRUPT_AUTOVECTOR_EX + irqline;
}


void m68000_init(void)
{
	C68k_Init(&C68K);
#if (EMU_SYSTEM == CPS1)
	C68k_Set_Fetch(&C68K, 0x000000, 0x1fffff, (u32)memory_region_cpu1);
	C68k_Set_Fetch(&C68K, 0x900000, 0x92ffff, (u32)cps1_gfxram);
	C68k_Set_Fetch(&C68K, 0xff0000, 0xffffff, (u32)cps1_ram);
	C68k_Set_ReadB(&C68K, m68000_read_memory_8);
	C68k_Set_ReadW(&C68K, m68000_read_memory_16);
	C68k_Set_WriteB(&C68K, m68000_write_memory_8);
	C68k_Set_WriteW(&C68K, m68000_write_memory_16);
#elif (EMU_SYSTEM == CPS2)
	C68k_Set_Fetch(&C68K, 0x000000, 0x3fffff, (u32)memory_region_user1);
	C68k_Set_Fetch(&C68K, 0x660000, 0x663fff, (u32)cps2_ram);
	C68k_Set_Fetch(&C68K, 0x900000, 0x92ffff, (u32)cps1_gfxram);
	C68k_Set_Fetch(&C68K, 0xff0000, 0xffffff, (u32)cps1_ram);
	C68k_Set_ReadB(&C68K, m68000_read_memory_8);
	C68k_Set_ReadW(&C68K, m68000_read_memory_16);
	C68k_Set_WriteB(&C68K, m68000_write_memory_8);
	C68k_Set_WriteW(&C68K, m68000_write_memory_16);
	C68k_Set_ReadB_PC_Relative(&C68K, m68000_read_pcrelative_8);
	C68k_Set_ReadW_PC_Relative(&C68K, m68000_read_pcrelative_16);
#elif (EMU_SYSTEM == MVS)
	C68k_Set_Fetch(&C68K, 0x000000, 0x0fffff, (u32)memory_region_cpu1);
	C68k_Set_Fetch(&C68K, 0x100000, 0x10ffff, (u32)neogeo_ram);
	if (memory_length_cpu1 > 0x100000)
		C68k_Set_Fetch(&C68K, 0x200000, 0x2fffff, (u32)&memory_region_cpu1[0x100000]);
	else
		C68k_Set_Fetch(&C68K, 0x200000, 0x2fffff, (u32)memory_region_cpu1);
	C68k_Set_Fetch(&C68K, 0xc00000, 0xc00000 + (memory_length_user1 - 1), (u32)memory_region_user1);
	C68k_Set_ReadB(&C68K, m68000_read_memory_8);
	C68k_Set_ReadW(&C68K, m68000_read_memory_16);
	C68k_Set_WriteB(&C68K, m68000_write_memory_8);
	C68k_Set_WriteW(&C68K, m68000_write_memory_16);
#endif
	C68k_Set_IRQ_Callback(&C68K, m68000_irq_callback);
}


void m68000_reset(void)
{
	irq_state = CLEAR_LINE;
	C68k_Reset(&C68K);
}


void m68000_exit(void)
{
	/* nothing to do ? */
}


int m68000_execute(int cycles)
{
	return C68k_Exec(&C68K, cycles);
}


void m68000_set_irq_line(int irqline, int state)
{
	if (irqline == IRQ_LINE_NMI)
		irqline = 7;

	irq_state = state;

	switch (state)
	{
	case CLEAR_LINE:
		C68k_Set_IRQ(&C68K, 0);
		return;

	case ASSERT_LINE:
		C68k_Set_IRQ(&C68K, irqline);
		return;

	default:
		C68k_Set_IRQ(&C68K, irqline);
		return;
	}
}


/*------------------------------------------------------
	セーブ/ロード ステート
------------------------------------------------------*/
int get_m68000_size(void){
	
	int i;
	int m68size = 0;
	
	for (i = 0; i < 8; i++){
		
		m68size += 4;//state_save_long(&C68K.D[i], 1);
	}
	for (i = 0; i < 8; i++){
		m68size += 4;//state_save_long(&C68K.A[i], 1);
	}

	m68size += 4;//state_save_long(&C68K.flag_C, 1);
	m68size += 4;//state_save_long(&C68K.flag_V, 1);
	m68size += 4;//state_save_long(&C68K.flag_Z, 1);
	m68size += 4;//state_save_long(&C68K.flag_N, 1);
	m68size += 4;//state_save_long(&C68K.flag_X, 1);
	m68size += 4;//state_save_long(&C68K.flag_I, 1);
	m68size += 4;//state_save_long(&C68K.flag_S, 1);
	m68size += 4;//state_save_long(&C68K.USP, 1);
	m68size += 4;//state_save_long(&pc, 1);
	m68size += 4;//state_save_long(&C68K.HaltState, 1);
	m68size += 4;//state_save_long(&C68K.IRQLine, 1);
	m68size += 4;//state_save_long(&irq_state, 1);
	
	return m68size;
	
}

int save_m68000_to_mem( unsigned char* stor){
	unsigned char *pos_ini = stor;
	int i;
	u32 pc = C68k_Get_PC(&C68K);

	for (i = 0; i < 8; i++){
		//state_save_long(&C68K.D[i], 1);
		memcpy( stor, &C68K.D[i], 4);
		stor += 4;
	}
	for (i = 0; i < 8; i++){
		//state_save_long(&C68K.A[i], 1);
		memcpy( stor, &C68K.A[i], 4);
		stor += 4;
	}

	//state_save_long(&C68K.flag_C, 1);
	memcpy( stor, &C68K.flag_C, 4);
	stor += 4;
	
	//state_save_long(&C68K.flag_V, 1);
	memcpy( stor,&C68K.flag_V, 4);
	stor += 4;
	
	//state_save_long(&C68K.flag_Z, 1);
	memcpy( stor,&C68K.flag_Z, 4);
	stor += 4;
	
	
	//state_save_long(&C68K.flag_N, 1);
	memcpy( stor,&C68K.flag_N, 4);
	stor += 4;
	
	
	//state_save_long(&C68K.flag_X, 1);
	memcpy( stor,&C68K.flag_X, 4);
	stor += 4;
	
	//state_save_long(&C68K.flag_I, 1);
	memcpy( stor,&C68K.flag_I, 4);
	stor += 4;
	
	//state_save_long(&C68K.flag_S, 1);
	memcpy( stor,&C68K.flag_S, 4);
	stor += 4;
	
	//state_save_long(&C68K.USP, 1);
	memcpy( stor,&C68K.USP, 4);
	stor += 4;
	
	//state_save_long(&pc, 1);
	memcpy( stor,&pc, 4);
	stor += 4;
	
	//state_save_long(&C68K.HaltState, 1);
	memcpy( stor,&C68K.HaltState, 4);
	stor += 4;
	
	//state_save_long(&C68K.IRQLine, 1);
	memcpy( stor,&C68K.IRQLine, 4);
	stor += 4;
	
	//state_save_long(&irq_state, 1);
	memcpy( stor,&irq_state, 4);
	stor += 4;
	
	return (stor - pos_ini);
}

#ifdef SAVE_STATE

STATE_SAVE( m68000 )
{
	int i;
	u32 pc = C68k_Get_PC(&C68K);

	for (i = 0; i < 8; i++)
		state_save_long(&C68K.D[i], 1);
	for (i = 0; i < 8; i++)
		state_save_long(&C68K.A[i], 1);

	state_save_long(&C68K.flag_C, 1);
	state_save_long(&C68K.flag_V, 1);
	state_save_long(&C68K.flag_Z, 1);
	state_save_long(&C68K.flag_N, 1);
	state_save_long(&C68K.flag_X, 1);
	state_save_long(&C68K.flag_I, 1);
	state_save_long(&C68K.flag_S, 1);
	state_save_long(&C68K.USP, 1);
	state_save_long(&pc, 1);
	state_save_long(&C68K.HaltState, 1);
	state_save_long(&C68K.IRQLine, 1);
	state_save_long(&irq_state, 1);
}

STATE_LOAD( m68000 )
{
	int i;
	u32 pc;

	for (i = 0; i < 8; i++)
		state_load_long(&C68K.D[i], 1);
	for (i = 0; i < 8; i++)
		state_load_long(&C68K.A[i], 1);

	state_load_long(&C68K.flag_C, 1);
	state_load_long(&C68K.flag_V, 1);
	state_load_long(&C68K.flag_Z, 1);
	state_load_long(&C68K.flag_N, 1);
	state_load_long(&C68K.flag_X, 1);
	state_load_long(&C68K.flag_I, 1);
	state_load_long(&C68K.flag_S, 1);
	state_load_long(&C68K.USP, 1);
	state_load_long(&pc, 1);
	state_load_long(&C68K.HaltState, 1);
	state_load_long(&C68K.IRQLine, 1);
	state_load_long(&irq_state, 1);

	C68k_Set_PC(&C68K, pc);
}

#endif /* SAVE_STATE */

int load_m68000_from_mem( unsigned char* stor){
	unsigned char *pos_ini = stor;
	int i;
	u32 pc;

	for (i = 0; i < 8; i++){
		//state_load_long(&C68K.D[i], 1);
		memcpy( &C68K.D[i], stor,  4);
		stor += 4;
	}
	for (i = 0; i < 8; i++){
		//state_load_long(&C68K.A[i], 1);
		memcpy( &C68K.A[i],stor,  4);
		stor += 4;
	}

	//state_load_long(&C68K.flag_C, 1);
	memcpy( &C68K.flag_C, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.flag_V, 1);
	memcpy(&C68K.flag_V, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.flag_Z, 1);
	memcpy(&C68K.flag_Z, stor, 4);
	stor += 4;
	
	
	//state_load_long(&C68K.flag_N, 1);
	memcpy(&C68K.flag_N, stor, 4);
	stor += 4;
	
	
	//state_load_long(&C68K.flag_X, 1);
	memcpy(&C68K.flag_X, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.flag_I, 1);
	memcpy(&C68K.flag_I, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.flag_S, 1);
	memcpy(&C68K.flag_S, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.USP, 1);
	memcpy(&C68K.USP, stor, 4);
	stor += 4;
	
	//state_load_long(&pc, 1);
	memcpy(&pc, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.HaltState, 1);
	memcpy(&C68K.HaltState, stor, 4);
	stor += 4;
	
	//state_load_long(&C68K.IRQLine, 1);
	memcpy(&C68K.IRQLine, stor, 4);
	stor += 4;
	
	//state_load_long(&irq_state, 1);
	memcpy(&irq_state, stor, 4);
	stor += 4;
	
	C68k_Set_PC(&C68K, pc);
	
	return (stor - pos_ini);
}


