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

	memintrf.c

	Memory interace

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

#include "ncdz.h"


#define M68K_AMASK 0x00ffffff
#define Z80_AMASK 0x0000ffff


/***************************************************************************
	Global variables
 **************************************************************************/

u8 *neogeo_prg_memory;
u8 *neogeo_z80_memory;
u8 *neogeo_fix_memory;
u8 *neogeo_spr_memory;
u8 *neogeo_pcm_memory;
u8 *neogeo_rom_memory;
u8 neogeo_memorycard[0x2000];


/***************************************************************************
	Function prptotypes
 **************************************************************************/

static int neogeo_load_rom(void);


static void errormsg(const char *mem_name)
{
#if JAPANESE_UI
	ui_popup("%sメモリを確保中にエラーが発生しました。", mem_name);
#else
	ui_popup("Could not allocating %s memory.", mem_name);
#endif
}

/*------------------------------------------------------
	Initialize memory interface
 -----------------------------------------------------*/

int memory_init(void)
{
	FILE *fp;
	char path[MAX_PATH];

	fatal_error = 1;

	if ((neogeo_prg_memory = (u8 *)calloc(1, 0x200000)) == NULL)
	{
		errormsg("PRG");
		return 0;
	}

	if ((neogeo_z80_memory = (u8 *)calloc(1, 0x10000)) == NULL)
	{
		errormsg("Z80");
		return 0;
	}

	if ((neogeo_fix_memory = (u8 *)calloc(1, 0x20000)) == NULL)
	{
		errormsg("FIX");
		return 0;
	}

	if ((neogeo_spr_memory = (u8 *)calloc(1, 0x400000)) == NULL)
	{
		errormsg("SPR");
		return 0;
	}

	if ((neogeo_pcm_memory = (u8 *)calloc(1, 0x100000)) == NULL)
	{
		errormsg("PCM");
		return 0;
	}

	if ((neogeo_rom_memory = (u8 *)calloc(1, 0x80000)) == NULL)
	{
		errormsg("ROM");
		return 0;
	}

	fatal_error = 0;

	memset(neogeo_memorycard, 0, 0x2000);

	sprintf(path, "%s%s", launchDir, "backup.bin");

	if ((fp = fopen(path, "rb")) != NULL)
	{
		fread(neogeo_memorycard, 0x2000, 1, fp);
		fclose(fp);
	}

	return neogeo_load_rom();
}


/*------------------------------------------------------
	Shutdown memory interface
 -----------------------------------------------------*/

void memory_shutdown(void)
{
	FILE *fp;
	char path[MAX_PATH];

	sprintf(path, "%s%s", launchDir, "backup.bin");

	if ((fp = fopen(path, "wb")) != NULL)
	{
		fwrite(neogeo_memorycard, 0x2000, 1, fp);
		fclose(fp);
	}

	free(neogeo_rom_memory);
	free(neogeo_pcm_memory);
	free(neogeo_spr_memory);
	free(neogeo_fix_memory);
	free(neogeo_z80_memory);
	free(neogeo_prg_memory);
}


/*------------------------------------------------------
	Load BIOS rom
 -----------------------------------------------------*/

static int neogeo_load_rom(void)
{
	FILE *fp;
	char path[MAX_PATH];
	u16 *mem16;

	sprintf(path, "%s%s", launchDir, "neocd.bin");

	if ((fp = fopen(path, "rb")) != NULL)
	{
		fread(neogeo_rom_memory, 0x80000, 1, fp);
		fclose(fp);

		if (crc32(0, neogeo_rom_memory, 0x80000) != 0xdf9de490)
		{
#if JAPANESE_UI
			ui_popup("BIODイメージ(neocd.bin)が異常です。");
#else
			ui_popup("Invalid BIOS image (neocd.bin) found.");
#endif
			fatal_error = 1;
			return 0;
		}
	}
	else
	{
#if JAPANESE_UI
		ui_popup("BIODイメージ(neocd.bin)がありません。");
#else
		ui_popup("BIOS image (neocd.bin) not found.");
#endif
		fatal_error = 1;
		return 0;
	}

	mem16 = (u16 *)neogeo_rom_memory;

	// load CD-ROM
	mem16[0xdb70 >> 1] = 0xfac0;
	mem16[0xdb72 >> 1] = 0x4e75;

	// CDDA control
	mem16[0x056a >> 1] = 0xfac1;
	mem16[0x056c >> 1] = 0x4e75;

	// exit
	mem16[0x055e >> 1] = 0xfac2;
	mem16[0x0560 >> 1] = 0x4e75;

	// CD-ROM protect
	mem16[0xb040 >> 1] = 0x4e71;
	mem16[0xb042 >> 1] = 0x4e71;

	// reset
	mem16[0xa87a >> 1] = 0x4239;
	mem16[0xa87c >> 1] = 0x0010;
	mem16[0xa87e >> 1] = 0xfdae;

	return 1;
}


/*------------------------------------------------------
	MC68000 read memory (byte)
 -----------------------------------------------------*/

u32 m68k_read_memory_8(u32 offset)
{
	offset &= M68K_AMASK;

	if (offset < 0x200000)
        return neogeo_prg_memory[offset ^ 1];

	int shift = (~offset & 1) << 3;
	u16 mem_mask = ~(0xff << shift);

	switch (offset >> 16)
    {
	case 0x30: return read_inputport1();
	case 0x32: return neogeo_z80_r(offset >> 1, mem_mask) >> shift;
	case 0x34: return read_inputport2();
	case 0x38: return read_inputport3();
	case 0x3c: return neogeo_video_16_r(offset >> 1, mem_mask) >> shift;

	case 0x80:
		offset &= 0x3fff;
		return neogeo_memorycard[offset >> 1];

	case 0xc0: case 0xc1: case 0xc2: case 0xc3:
	case 0xc4: case 0xc5: case 0xc6: case 0xc7:
		offset &= 0x7ffff;
		return neogeo_rom_memory[offset ^ 1];

	case 0xe0: case 0xe1: case 0xe2: case 0xe3:
	case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb:
	case 0xec: case 0xed: case 0xee: case 0xef:
		offset &= 0xfffff;
    	return neogeo_externalmem_16_r(offset >> 1, mem_mask) >> shift;

	case 0xff:
		offset &= 0x1ff;
		return neogeo_hardcontrol_16_r(offset >> 1, mem_mask) >> shift;
	}

    return 0xff;
}


/*------------------------------------------------------
	MC68000 read memory (word)
 -----------------------------------------------------*/

u32 m68k_read_memory_16(u32 offset)
{
	offset &= M68K_AMASK;

	if (offset < 0x200000)
		return *(u16 *)&neogeo_prg_memory[offset];

	switch (offset >> 16)
	{
	case 0x30: return read_inputport1() << 8;
	case 0x32: return neogeo_z80_r(offset >> 1, 0);
	case 0x34: return read_inputport2() << 8;
	case 0x38: return read_inputport3() << 8;
	case 0x3c: return neogeo_video_16_r(offset >> 1, 0);

	case 0x40:
		offset &= 0x1fff;
		return neogeo_paletteram16[offset >> 1];

	case 0x80:
		offset &= 0x3fff;
		return neogeo_memorycard[offset >> 1] | 0xff00;

	case 0xc0: case 0xc1: case 0xc2: case 0xc3:
	case 0xc4: case 0xc5: case 0xc6: case 0xc7:
		offset &= 0x7ffff;
		return *(u16 *)&neogeo_rom_memory[offset];

	case 0xe0: case 0xe1: case 0xe2: case 0xe3:
	case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb:
	case 0xec: case 0xed: case 0xee: case 0xef:
		offset &= 0xfffff;
		return neogeo_externalmem_16_r(offset >> 1, 0);

	case 0xff:
		offset &= 0x1ff;
		return neogeo_hardcontrol_16_r(offset >> 1, 0);
	}

	return 0xffff;
}


/*------------------------------------------------------
	MC68000 read memory (long)
 -----------------------------------------------------*/

u32 m68k_read_memory_32(u32 offset)
{
	u32 data;

	offset &= M68K_AMASK;
	data  = m68k_read_memory_16(offset) << 16;
	data |= m68k_read_memory_16(offset + 2);

	return data;
}


/*------------------------------------------------------
	MC68000 write memory (byte)
 -----------------------------------------------------*/

void m68k_write_memory_8(u32 offset, u32 data)
{
	offset &= M68K_AMASK;
	data &= 0xff;

	if (offset < 0x200000)
	{
		neogeo_prg_memory[offset ^ 1] = data;
		return;
	}

	int shift = (~offset & 1) << 3;
	u16 mem_mask = ~(0xff << shift);

	switch (offset >> 16)
	{
	case 0x30: watchdog_counter = 3 * FPS; break;
	case 0x32: neogeo_z80_w(offset >> 1, data << shift, mem_mask); break;
	case 0x3a: neogeo_syscontrol2_16_w(offset >> 1, data << shift, mem_mask); break;
	case 0x3c: neogeo_video_16_w(offset >> 1, data << shift, mem_mask); break;

	case 0x80:
		offset &= 0x3fff;
		if (ACCESSING_LSB) neogeo_memorycard[offset >> 1] = data & 0xff;
		break;

	case 0xe0: case 0xe1: case 0xe2: case 0xe3:
	case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb:
	case 0xec: case 0xed: case 0xee: case 0xef:
		offset &= 0xfffff;
		neogeo_externalmem_16_w(offset >> 1, data << shift, mem_mask);
		return;

	case 0xff:
		offset &= 0x1ff;
		neogeo_hardcontrol_16_w(offset >> 1, data << shift, mem_mask);
		return;
	}
}


/*------------------------------------------------------
	MC68000 write memory (word)
 -----------------------------------------------------*/

void m68k_write_memory_16(u32 offset, u32 data)
{
	offset &= M68K_AMASK;
	data &= 0xffff;

	if (offset < 0x200000)
	{
		*(u16 *)&neogeo_prg_memory[offset] = data;
		return;
	}

	switch (offset >> 16)
	{
	case 0x30: watchdog_counter = 3 * FPS;  break;
	case 0x32: neogeo_z80_w(offset >> 1, data, 0); return;
	case 0x3a: neogeo_syscontrol2_16_w(offset >> 1, data, 0); return;
	case 0x3c: neogeo_video_16_w(offset >> 1, data, 0); return;

	case 0x40:
		offset &= 0x1fff;
		neogeo_paletteram16_w(offset >> 1, data, 0);
		return;

	case 0x80:
		offset &= 0x3fff;
		neogeo_memorycard[offset >> 1] = data & 0xff;
		return;

	case 0xe0: case 0xe1: case 0xe2: case 0xe3:
	case 0xe4: case 0xe5: case 0xe6: case 0xe7:
	case 0xe8: case 0xe9: case 0xea: case 0xeb:
	case 0xec: case 0xed: case 0xee: case 0xef:
		offset &= 0xfffff;
		neogeo_externalmem_16_w(offset >> 1, data, 0);
		return;

	case 0xff:
		offset &= 0x1ff;
		neogeo_hardcontrol_16_w(offset >> 1, data, 0);
		return;
	}
}


/*------------------------------------------------------
	MC68000 write memory (long)
 -----------------------------------------------------*/

void m68k_write_memory_32(u32 offset, u32 data)
{
	offset &= M68K_AMASK;
	m68k_write_memory_16(offset + 0, data >> 16);
	m68k_write_memory_16(offset + 2, data & 0xffff);
}


#ifndef MAME_Z80

/*------------------------------------------------------
	Z80 read memory (byte)
 -----------------------------------------------------*/

u32 z80_read_memory_8(u32 offset)
{
	offset &= Z80_AMASK;
	return neogeo_z80_memory[offset];
}


/*------------------------------------------------------
	Z80 write memory (byte)
 -----------------------------------------------------*/

void z80_write_memory_8(u32 offset, u32 data)
{
	offset &= Z80_AMASK;
	neogeo_z80_memory[offset] = data;
}

#endif
