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

	driver.c

	NEOGEO CDZ driver (based on M.A.M.E.'s neogeo driver)

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

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

    NEOGEO CDZ Motherboard
    ----------------------

    NEO-CDC1-1
                    L   R   V    S-V    A/V             DC
    |--------------| |-| |-| |--|  |---|   |-----------|  |---------------|
    |              +-+ +-+ +-+  +--+   +---+           +--+               |
    |    D05                   MB3516A                                    |
    |                                                                     |
    |                                                                     |
    |                   74LS05                  518129                    |
    |                            NEO-GRC2-F                               |
    |                                           000-LO                    |
    |                                                                     |
    |      D357J5             LH516  TOP-SPD              LH52B256N       |
    |                                                                     |
    |   KDS-5K                          HC245A    514400  LH52B256N       |
    |  24.000MHz                                                          |
    |                  LC89513K 51832   HC245A    514400  84C00AM-6       |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |  D05  74HCU04A   HC245A HC245A LS05 HC245A HC245A                   |
    |                                                  HC245A             |
    |     |=====|     +----------+        +----------+                    |
    |-----------------|          |--------|          |--------------------|
                       Controler1          Controler2

    (Back)
    |---------------------------------------------------------------------|
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                         LS273  LS273    9480F       |
    |                                                                     |
    |  5118160   NEO-0FC   GC24512          LH52B256N                     |
    |                                                   5118160           |
    |                                       LH52B256N                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |  5118160   NEO-YSA       MC68HC000                                  |
    |                                        NEO-MGA2-SA                  |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                                                                     |
    |                 +----------+        +----------+                    |
    |-----------------|          |--------|          |--------------------|
                       Controler2          Controler1

    Notes:
          68k clock: 12.000MHz
          Z80 clock: 6.000MHz
       YM2610 clock: 8.000MHz
              VSync: 60Hz
              HSync: 15.21kHz

             Custom SNK chips
             -------------------
             NEO-GRC2-F
             NEO-0FC
             NEO-YSA   (YM2610?)
             NEO-MGA2-SA
             GC24512

             ROMs        Type
             ----------------------------
             TOP-SPD     TC534200 (DIP40)
             000-L0      TC531000 (DIP28)

             CD-ROM controler
             -----------------
             LC89513K: SANYO

=============================================================================

Points to note, known and proven information deleted from this map:

    0x3000001       Dipswitches
                bit 0 : Selftest
                bit 1 : Unknown (Unused ?) \ something to do with
                bit 2 : Unknown (Unused ?) / auto repeating keys ?
                bit 3 : \
                bit 4 :  | communication setting ?
                bit 5 : /
                bit 6 : free play
                bit 7 : stop mode ?

    0x3c000c        IRQ acknowledge

    0x380011        Backup bank select

    0x3a0001        Enable display.
    0x3a0011        Disable display

    0x3a0003        Swap in Bios (0x80 bytes vector table of BIOS)
    0x3a0013        Swap in Rom  (0x80 bytes vector table of ROM bank)

    0x3a000d        lock backup ram
    0x3a001d        unlock backup ram

    0x3a000b        set game vector table (?)  mirror ?
    0x3a001b        set bios vector table (?)  mirror ?

    0x3a000c        Unknown (ghost pilots)
    0x31001c        Unknown (ghost pilots)

    IO word read

    0x3c0002        return vidram word (pointed to by 0x3c0000)
    0x3c0006        ?????.
    0x3c0008        shadow adress for 0x3c0000 (not confirmed).
    0x3c000a        shadow adress for 0x3c0002 (confirmed, see
                               Puzzle de Pon).
    IO word write

    0x3c0006        Unknown, set vblank counter (?)

    0x3c0008        shadow address for 0x3c0000 (not confirmed)
    0x3c000a        shadow address for 0x3c0002 (not confirmed)

    NEOGEO CDZではNEC 4990は使用されておらず、別のチップが使用されていると
    思われます。Z80のポート$C0〜$02に出力されている値が制御を行っているように
    思えますが、ハードウェアには疎いのでさっぱりわからず。

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ncdz.h"


/* VBLANK should fire on line 248 */
#define RASTER_COUNTER_START 0x1f0	/* value assumed right after vblank */
#define RASTER_COUNTER_RELOAD 0x0f8	/* value assumed after 0x1ff */
#define RASTER_LINE_RELOAD (0x200 - RASTER_COUNTER_START)

#define SCANLINE_ADJUST 3	/* in theory should be 0, give or take an off-by-one mistake */

/*  flags for irq1control:

    0x07 unused? kof94 sets some random combination of these at the character
         selection screen but only because it does andi.w #$ff2f, $3c0006. It
         clears them immediately after.

    0x08 shocktro2, stops autoanim counter

    Maybe 0x07 writes to the autoanim counter, meaning that in conjunction with
    0x08 one could fine control it. However, if that was the case, writing the
    the IRQ2 control bits would interfere with autoanimation, so I'm probably
    wrong.

    0x10 irq1 enable, tile engine scanline irq that is triggered
    when a certain scanline is reached.

    0x20 when set, the next values written in the irq position register
    sets irq1 to happen N lines after the current one

    0x40 when set, irq position register is automatically loaded at vblank to
    set the irq1 line.

    0x80 when set, every time irq1 is triggered the irq position register is
    automatically loaded to set the next irq1 line.

    0x80 and 0x40 may be set at the same time (Viewpoint does this).
*/

#define IRQ1CTRL_AUTOANIM_STOP		0x08
#define IRQ1CTRL_ENABLE				0x10
#define IRQ1CTRL_LOAD_RELATIVE		0x20
#define IRQ1CTRL_AUTOLOAD_VBLANK	0x40
#define IRQ1CTRL_AUTOLOAD_REPEAT	0x80


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

const char default_name[16] = "default";
const char *game_name;
int game_index;
int driver_type;
int neogeo_ngh_number;
u32 neogeo_frame_counter;
int watchdog_counter;


/***************************************************************************
	Global structures
 **************************************************************************/

struct rectangle visible_area = { 8, 311, FIRST_VISIBLE_LINE, LAST_VISIBLE_LINE };


/***************************************************************************
	Private variables
 **************************************************************************/

static int neogeo_raster_enable;

static int current_rasterline;
static int current_rastercounter;
static int irq1start;
static int irq1control;
static u32 irq1pos_value;
static int scanline_read;
static int vblank_int;
static int scanline_int;
static int hack_irq;

static u32 frame_counter;
static u32 neogeo_frame_counter_speed;

static u8  exmem[0x100];
static u8  exmem_latch[0x100];
static u8  exmem_bank[0x10];
static u8  exmem_counter;

static u8  upload_mode;
static u8  upload_type;
static u32 upload_offset1;
static u32 upload_offset2;
static u32 upload_length;
static u16 upload_pattern;
static int upload_executing;
static u32 z80_cdda_offset;

static int sound_code;
static int result_code;
static int pending_command;

static u8 memory_region_hctrl[0x200];
static u8 neogeo_game_vectors[0x100];


/*------------------------------------------------------
	Inisialize driver
 -----------------------------------------------------*/

void neogeo_driver_init(void)
{
	memset(memory_region_hctrl, 0, sizeof(memory_region_hctrl));
	z80_cdda_offset = 0;
}


/*------------------------------------------------------
	Reset driver
 -----------------------------------------------------*/

void neogeo_driver_reset(void)
{
	irq1start = 1000;
	irq1control = 0;
	irq1pos_value = 0;

	current_rasterline = 0;
	current_rastercounter = RASTER_COUNTER_START;

	scanline_read = 0;

	vblank_int = 0;
	scanline_int = 0;

	neogeo_raster_enable = 0;
	frame_counter = 0;

	sound_code = 0;
	result_code = 0;
	pending_command = 0;

	watchdog_counter = -1;

	neogeo_frame_counter = 0;
	neogeo_frame_counter_speed = 4;

	memset(exmem, 0, sizeof(exmem));
	memset(exmem_latch, 0, sizeof(exmem_latch));
	memset(exmem_bank, 0, sizeof(exmem_bank));
	exmem_counter = 0;

	upload_mode = UPLOAD_IMMIDIATE;
	upload_type = UNKNOWN_TYPE;
	upload_offset1 = 0;
	upload_offset2 = 0;
	upload_length = 0;
	upload_pattern = 0;
	upload_executing = 0;
}


/*------------------------------------------------------
	Reset driver
 -----------------------------------------------------*/

void neogeo_check_game(void)
{
	int i, NGH_number;

	memcpy(neogeo_game_vectors, neogeo_prg_memory, 0x100);

	game_index = 0;
	neogeo_ngh_number = 0;
	game_name = default_name;
	hack_irq = 0;

	NGH_number = m68k_read_memory_16(0x108);

	for (i = 0; games[i].ngh_number; i++)
	{
		if (games[i].ngh_number == NGH_number)
		{
			game_index = i + 1;
			neogeo_ngh_number = NGH_number;
			game_name = games[i].name;
			driver_type = games[i].type;

			if (NGH_NUMBER(0x0243))	// lastbld2
				hack_irq = 1;

			break;
		}
	}
}


/*------------------------------------------------------
	Update interrupt
 -----------------------------------------------------*/

static void update_interrupts(void)
{
	int level = 0;

	/* determine which interrupt is active */
	if (vblank_int) level = 2;
	if (scanline_int) level = 1;

	/* either set or clear the appropriate lines */
	m68000_set_irq_line(level, level ? ASSERT_LINE : CLEAR_LINE);

	if (hack_irq)
	{
		if (level == 2) vblank_int = 0;
		if (level == 1) scanline_int = 0;
	}
}


/*------------------------------------------------------
	MC68000 VBLANK interrupt (IRQ2)
 -----------------------------------------------------*/

INTERRUPT_GEN( neogeo_interrupt )
{
	current_rasterline = 0;
	current_rastercounter = RASTER_COUNTER_START;

	if (irq1control & IRQ1CTRL_AUTOLOAD_VBLANK)
		irq1start = (irq1pos_value + 3) / 0x180;	/* ridhero gives 0x17d */
	else
		irq1start = 1000;

	if (!(irq1control & IRQ1CTRL_AUTOANIM_STOP))
	{
		/* Animation counter */
		if (frame_counter++ > neogeo_frame_counter_speed)	/* fixed animation speed */
		{
			frame_counter = 0;
			neogeo_frame_counter++;
		}
	}

	vblank_int = 1; /* vertical blank */

	if (hack_irq)
	{
		// 月華の剣士2のオープニングBのフリーズを回避
		// 原因：1フレーム内で処理が間に合わず、描画処理を行っている時にVBLANK割り込みが発生するため。
		//       実際には割り込みを行わないデータをどこかに書き込んでいるはず。
		u32 pc = m68000_get_reg(M68K_PC);

		if (pc >= 0xbf00 && pc <= 0xbfff)	// 0xbf92
			vblank_int = 0;
	}

	update_interrupts();
}


/*------------------------------------------------------
	MC68000 scanline interrupt (IRQ1)
 -----------------------------------------------------*/

#ifdef EMULATE_RASTER_EFFECT
INTERRUPT_GEN( neogeo_raster_interrupt )
{
	int do_refresh = 0;

	current_rasterline = param;

	if (current_rasterline == RASTER_LINES) current_rasterline = 0;	/* vblank */

	if (current_rasterline < RASTER_LINE_RELOAD)
		current_rastercounter = RASTER_COUNTER_START + current_rasterline;
	else
		current_rastercounter = RASTER_COUNTER_RELOAD + current_rasterline - RASTER_LINE_RELOAD;

	if (irq1control & IRQ1CTRL_ENABLE)
	{
		if (param == irq1start)
		{
			do_refresh = 1;

			if (irq1control & IRQ1CTRL_AUTOLOAD_REPEAT)
				irq1start += (irq1pos_value + 3) / 0x180;	/* ridhero gives 0x17d */

			scanline_int = 1;
		}
	}

	if (param == RASTER_LINES)
	{
		if (irq1control & IRQ1CTRL_AUTOLOAD_VBLANK)
			irq1start = (irq1pos_value + 3) / 0x180;	/* ridhero gives 0x17d */
		else
			irq1start = 1000;

		if (!(irq1control & IRQ1CTRL_AUTOANIM_STOP))
		{
			/* Animation counter */
			if (frame_counter++ > neogeo_frame_counter_speed)	/* fixed animation speed */
			{
				frame_counter = 0;
				neogeo_frame_counter++;
			}
		}

		vblank_int = 1; /* vertical blank */
	}

	if (do_refresh && !skip_this_frame())
	{
		if (param > RASTER_LINE_RELOAD)	/* avoid unnecessary updates after start of vblank */
			neogeo_partial_screenrefresh((current_rastercounter - 256) - 1 + SCANLINE_ADJUST);
	}

	update_interrupts();

	if (param + 1 != RASTER_LINES)
		timer_adjust(SECS_PER_SCANLINE, param + 1, neogeo_raster_interrupt);
}
#endif


/*------------------------------------------------------
	MC68000 interrupt (aof2)
 -----------------------------------------------------*/

INTERRUPT_GEN( neogeo_interrupt_aof2 )
{
	/*
		龍虎の拳2

		ゲームスピードが85%時、通常の方法では正確なゲームスピードを再現できない
		(100%と同じスピードになってしまう)ため、専用の割り込みルーチンを用意。
		割り込み回数を最小限にするために、タイマで割り込みを管理します。
		irq1startの値は、次の割り込みまでのスキャンライン数です。
	*/

	static int prev_irq1 = 0;

	if (param == RASTER_LINES)
	{
		current_rasterline = 0;
		current_rastercounter = RASTER_COUNTER_START;

		if (irq1control & IRQ1CTRL_ENABLE)
			irq1start = (irq1pos_value + 3) / 0x180;
		else
			irq1start = 1000;

		if (!(irq1control & IRQ1CTRL_AUTOANIM_STOP))
		{
			/* Animation counter */
			if (frame_counter++ > neogeo_frame_counter_speed)
			{
				frame_counter = 0;
				neogeo_frame_counter++;
			}
		}

		if (!(irq1control & IRQ1CTRL_ENABLE))
		{
			prev_irq1 = 0;
		}
		else if (prev_irq1 == 0)
		{
			prev_irq1 = 1;
			timer_adjust((double)irq1start * SECS_PER_SCANLINE, 0, neogeo_interrupt_aof2);
		}

		vblank_int = 1; /* vertical blank */
	}
	else
	{
		if (irq1control & IRQ1CTRL_ENABLE)
		{
			scanline_int = 1;
			timer_adjust((double)irq1start * SECS_PER_SCANLINE, 0, neogeo_interrupt_aof2);
		}
	}

	update_interrupts();
}


/****************************************************************************
	MC68K memory access
 ***************************************************************************/

/*------------------------------------------------------
	Select BIOS vector (0x3a0003)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_select_bios_vectors )
{
	memcpy(neogeo_prg_memory, neogeo_rom_memory, 0x100);
}


/*------------------------------------------------------
	Select game vector (0x3a0013)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_select_game_vectors )
{
	memcpy(neogeo_prg_memory, neogeo_game_vectors, 0x100);
}


/*------------------------------------------------------
	Select palette RAM bank  (0x3a000f - bank 0)
						     (0x3a001f - bank 1)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_setpalbank_16_w )
{
	if (neogeo_palette_index != offset)
	{
		neogeo_palette_index = offset;
		neogeo_paletteram16 = neogeo_palettebank16[offset];
		video_palette16 = video_palettebank16[offset];

		blit_fix_resetcache();
		blit_spr_resetcache();
	}
}


/*------------------------------------------------------
	Write VRAM offset ($3c0000)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_vidram16_offset_w )
{
	COMBINE_DATA(&neogeo_vidram16_offset);
}


/*------------------------------------------------------
	Read data from VRAM ($3c0000, $3c0002)
 -----------------------------------------------------*/

INLINE READ16_HANDLER( neogeo_vidram16_data_r )
{
	return neogeo_vidram16[neogeo_vidram16_offset];
}


/*------------------------------------------------------
	Write data to VRAM ($3c0002)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_vidram16_data_w )
{
	COMBINE_DATA(&neogeo_vidram16[neogeo_vidram16_offset]);

	neogeo_vidram16_offset = (neogeo_vidram16_offset & 0x8000)
			| ((neogeo_vidram16_offset + neogeo_vidram16_modulo) & 0x7fff);
}


/*------------------------------------------------------
	Read VRAM moduro ($3c0004)
 -----------------------------------------------------*/

INLINE READ16_HANDLER( neogeo_vidram16_modulo_r )
{
	return neogeo_vidram16_modulo;
}


/*------------------------------------------------------
	Write VRAM moduro ($3c0004)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_vidram16_modulo_w )
{
	COMBINE_DATA(&neogeo_vidram16_modulo);
}


/*---------------------------------------------------------
	Read video control data ($3c0006)
 --------------------------------------------------------*/

INLINE READ16_HANDLER( neogeo_control_16_r )
{
	int res;

//	scanline_read = 1;	/* needed for raster_busy optimization */

	if (driver_type >= CHECK_RASTERCOUNTER)
	{
		current_rasterline = 1 + (int)(timer_timeelapsed() / SECS_PER_SCANLINE);

		if (current_rasterline == RASTER_LINES)
			current_rasterline = 0;

		if (current_rasterline < RASTER_LINE_RELOAD)
			current_rastercounter = RASTER_COUNTER_START + current_rasterline;
		else
			current_rastercounter = RASTER_COUNTER_RELOAD + current_rasterline - RASTER_LINE_RELOAD;
	}

	res = ((current_rastercounter << 7) & 0xff80) |	/* raster counter */
		   (neogeo_frame_counter & 0x0007);			/* frame counter */

	return res;
}


/*---------------------------------------------------------
	Write video control data ($3c0006)
 --------------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_control_16_w )
{
	/* Auto-Anim Speed Control */
	neogeo_frame_counter_speed = (data >> 8) & 0xff;

	irq1control = data & 0xff;
}


/*------------------------------------------------------
	Write IRQ position     (0x3c0008, 0x3c000a)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_irq1pos_16_w )
{
	if (offset)
		irq1pos_value = (irq1pos_value & 0xffff0000) | (u32)data;
	else
		irq1pos_value = (irq1pos_value & 0x0000ffff) | ((u32)data << 16);

	if (irq1control & IRQ1CTRL_LOAD_RELATIVE)
	{
		int line = (irq1pos_value + 0x3b) / 0x180;

		if (driver_type >= CHECK_RASTERCOUNTER)
		{
			current_rasterline = 1 + (int)(timer_timeelapsed() / SECS_PER_SCANLINE);

			if (current_rasterline == RASTER_LINES)
				current_rasterline = 0;
		}

		irq1start = current_rasterline + line;
	}
}


/*------------------------------------------------------
	Write IRQ acknowledge ($3c000c)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( neogeo_irqack_16_w )
{
	if (ACCESSING_LSB)
	{
		if (data & 4) vblank_int = 0;
		if (data & 2) scanline_int = 0;
		update_interrupts();
	}
}


/*------------------------------------------------------
	Hardware upload ($ff0061)
 -----------------------------------------------------*/

static WRITE16_HANDLER( hardware_upload_16_w )
{
	if (data == 0x00)	// Clear upload info
	{
		upload_mode    = UPLOAD_IMMIDIATE;
		upload_offset1 = 0;
		upload_offset2 = 0;
		upload_length  = 0;
	}
	else if (data == 0x40)	// Execute upload
	{
		int i;
		u8 *src, *dst;

		if (upload_mode == UPLOAD_MEMORY)
		{
			upload_type = upload_get_type() & 0x0f;
		}
		else
		{
			if (upload_offset1 >= 0x400000 && upload_offset1 < 0x500000)
			{
				upload_type = PAL_TYPE;
			}
			else
			{
				upload_type = UNKNOWN_TYPE;
			}
		}

		switch (upload_mode)
		{
		case UPLOAD_IMMIDIATE:
			// Write immidiate value (address value)
			// It's use in system check only.
			break;

		case UPLOAD_PATTERN:
			// Write pattern data
			// It's use in system check only
			if (upload_type == PAL_TYPE)
			{
				// Use this in BIOS inisialize.
				for (i = 0; i < upload_length; i++)
				{
					offset = upload_offset1 + (i * 2);
					m68k_write_memory_16(offset, upload_pattern);
				}
			}
			break;

		case UPLOAD_MEMORY:
			{
				// Upload from M68K memory to another memory
				// PCM and PAT are uploaded by BIOS program.
				u32 length;

				length = upload_length << 1;
				src = neogeo_prg_memory + upload_offset1;

				switch (upload_type & 0x0f)
				{
				case PRG_TYPE:
					dst = neogeo_prg_memory;
					memcpy(dst + upload_offset2, src, length);
#ifdef SAVE_STATE
//					state_prg_mark_upload();
#endif
					break;

				case FIX_TYPE:
					dst = neogeo_fix_memory;
					offset = upload_offset2 - 0xe00000;
					swab(src, dst + (offset >> 1), length);
					neogeo_decode_fix(dst, offset, length);
#ifdef SAVE_STATE
//					state_fix_mark_upload();
#endif
					break;

				case SPR_TYPE:
					dst = neogeo_spr_memory;
					offset = (upload_offset2 - 0xe00000) + (upload_get_bank() << 20);
					swab(src, dst + offset, length);
					neogeo_decode_spr(dst, offset, length);
					exmem_bank[EXMEM_OBJ] = ((offset + upload_get_length()) >> 20) & 0x03;
#ifdef SAVE_STATE
//					state_spr_mark_upload();
#endif
					break;

				case Z80_TYPE:
					dst = neogeo_z80_memory;
					offset = upload_offset2 - 0xe00000;
					swab(src, dst + (offset >> 1), length);
					break;

				case PAL_TYPE:
					for (i = 0; i < length; i++)
					{
						data = m68k_read_memory_16(upload_offset1 + i * 2);
						m68k_write_memory_16(upload_offset2 + i * 2, data);
					}
#ifdef SAVE_STATE
//					state_pal_mark_upload();
#endif
					break;
				}
			}
			break;

		case UPLOAD_FILE:
			// Upload file
			break;
		}
	}
}


/*------------------------------------------------------
	Set upload offset 1   ($ff0064)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( upload_offset1_16_w )
{
	if (offset)
		upload_offset1 = (upload_offset1 & 0xffff0000) | (u32)data;
	else
		upload_offset1 = (upload_offset1 & 0x0000ffff) | ((u32)data << 16);
}


/*------------------------------------------------------
	Set upload offset 2   ($ff0068)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( upload_offset2_16_w )
{
	if (offset)
		upload_offset2 = (upload_offset2 & 0xffff0000) | (u32)data;
	else
		upload_offset2 = (upload_offset2 & 0x0000ffff) | ((u32)data << 16);

	upload_mode = UPLOAD_MEMORY;
}


/*------------------------------------------------------
	Set upload pattern data   ($ff006c)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( upload_pattern_16_w )
{
	upload_pattern = data;
	upload_mode = UPLOAD_PATTERN;
}


/*------------------------------------------------------
	Set upload length  ($ff0070)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( upload_length_16_w )
{
	if (offset)
		upload_length = (upload_length & 0xffff0000) | (u32)data;
	else
		upload_length = (upload_length & 0x0000ffff) | ((u32)data << 16);
}


/*------------------------------------------------------
	Set external memory type ($ff0105)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( exmem_type_16_w )
{
	exmem[exmem_counter] = data;

	if (exmem[exmem_counter] == EXMEM_Z80)
	{
		z80_cdda_offset = m68k_read_memory_32(0x108000 + 0x76ea);
		if (z80_cdda_offset)
			z80_cdda_offset = (z80_cdda_offset - 0xe00000) >> 1;
	}

	if (exmem[exmem_counter] == EXMEM_OBJ) exmem_bank[EXMEM_OBJ] = 0;
	if (exmem[exmem_counter] == EXMEM_PCMA) exmem_bank[EXMEM_PCMA] = 0;
}


/*------------------------------------------------------
	Disable draw SPR plane ($ff0111)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( spr_disable_16_w )
{
	spr_disable = data & 0xff;
}


/*------------------------------------------------------
	Disable draw FIX plane ($ff0115)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( fix_disable_16_w )
{
	fix_disable = data & 0xff;
}


/*------------------------------------------------------
	Enable video output ($ff0119)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( video_enable_16_w )
{
	video_enable = data & 0xff;
}


/*------------------------------------------------------
	Write latch external memory  ($ff0121) - OBJ RAM
	                             ($ff0123) - PCM-A RAM
	                             ($ff0127) - Z80 RAM
	                             ($ff0129) - FIX RAM
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( exmem_latch_16_w )
{
	if (exmem_counter < 0xff)
	{
		exmem_counter++;
		exmem_latch[exmem_counter] = offset & 0x0f;
	}
}


/*------------------------------------------------------
	Clear latch external memory  ($ff0141) - OBJ RAM
	                             ($ff0143) - PCM-A RAM
	                             ($ff0147) - Z80 RAM
	                             ($ff0149) - FIX RAM
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( exmem_latch_clear_16_w )
{
	offset &= 0x0f;

	if (exmem_latch[exmem_counter] == offset)
	{
		if (exmem_counter > 0)
		{
			exmem_latch[exmem_counter] = EXMEM_UNKNOWN;
			exmem_counter--;
		}
	}
}


/*------------------------------------------------------
	Set uploading flag ($ff0161)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( upload_executing_16_w )
{
	upload_executing = data & 0xff;
}


/*------------------------------------------------------
	Z80 reset / enable ($ff0183)
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( z80_enable_16_w )
{
	if (neogeo_sound_enable)
	{
		if (data & 0xff)
			z80_enable(1);
		else
			z80_reset(0);
	}
}


/*------------------------------------------------------
	Set external memory bank  ($ff01a1) - OBJ RAM
	                          ($ff01a3) - PCM-A RAM
 -----------------------------------------------------*/

INLINE WRITE16_HANDLER( exmem_bank_16_w )
{
	offset &= 0x0f;
	exmem_bank[offset] = data & 0x03;
}


/****************************************************************************
	M68000 memory handlers
 ***************************************************************************/

/*------------------------------------------------------
	Read Z80 communication data ($320001)
 -----------------------------------------------------*/

READ16_HANDLER( neogeo_z80_r )
{
	u16 res = 0x0000;

	if (neogeo_sound_enable)
	{
		res |= result_code << 8;
		if (pending_command) res &= 0x7fff;
	}
	else
		res = 0x0100;

	return res;
}


/*------------------------------------------------------
	Write Z80 communication data ($320001)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_z80_w )
{
	if (neogeo_sound_enable)
	{
		sound_code = (data >> 8) & 0xff;
		pending_command = 1;

		z80_set_irq_line(IRQ_LINE_NMI, PULSE_LINE);
#ifdef MAME_Z80
		z80_set_irq_line(IRQ_LINE_NMI, CLEAR_LINE);
#endif
		z80_execute(400);
	}
}


/*------------------------------------------------------
	Write system control switch 2 ($3a0000 - $3a001f)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_syscontrol2_16_w )
{
	offset &= 0xf;

	switch (offset << 1)
	{
	case 0x00:	break;	// unknown
	case 0x02:	neogeo_select_bios_vectors(0, data, mem_mask); break;
	case 0x04:	break;	// unknown
	case 0x06:	break;	// unknown
	case 0x08:	break;	// unknown
	case 0x0a:	break;	// unknown
	case 0x0c:	break;	// unknown
	case 0x0e:	neogeo_setpalbank_16_w(0, data, mem_mask); break;
	case 0x10:	break;	// unknown
	case 0x12:	neogeo_select_game_vectors(0, data, mem_mask);break;
	case 0x14:	break;	// unknown
	case 0x16:	break;	// unknown
	case 0x18:	break;	// unknown
	case 0x1a:	break;	// unknown
	case 0x1c:	break;	// unknown
	case 0x1e:	neogeo_setpalbank_16_w(1, data, mem_mask); break;
	}
}


/*------------------------------------------------------
	Read VRAM register (0x3c0000 - 0x3c000f)
 -----------------------------------------------------*/

READ16_HANDLER( neogeo_video_16_r )
{
	u16 retdata = 0xffff;

	if (!ACCESSING_MSB)
	{
		return 0xff;
	}

	offset &= 0x3;

	switch (offset << 1)
	{
	case 0: retdata = neogeo_vidram16_data_r(0, mem_mask); break;
	case 2: retdata = neogeo_vidram16_data_r(0, mem_mask); break;
	case 4:	retdata = neogeo_vidram16_modulo_r(0, mem_mask); break;
	case 6:	retdata = neogeo_control_16_r(0, mem_mask); break;
	}

	return retdata;
}


/*------------------------------------------------------
	Write VRAM register (0x3c0000 - 0x3c000f)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_video_16_w )
{
	offset &= 0x7;

	switch (offset << 1)
	{
	case 0x0: neogeo_vidram16_offset_w(0, data, mem_mask); break;
	case 0x2: neogeo_vidram16_data_w(0, data, mem_mask); break;
	case 0x4: neogeo_vidram16_modulo_w(0, data, mem_mask); break;
	case 0x6: neogeo_control_16_w(0, data, mem_mask); break;
	case 0x8: neogeo_irq1pos_16_w(0, data, mem_mask); break;
	case 0xa: neogeo_irq1pos_16_w(1, data, mem_mask); break;
	case 0xc: neogeo_irqack_16_w(0, data, mem_mask); break;
	case 0xe: break; /* Unknown, see control_r */
	}
}


/*------------------------------------------------------
	Write to palette RAM (0x400000 - 0x40ffff)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_paletteram16_w )
{
	if (offset & 0x0f)
	{
		u16 newword, oldword;

		offset &= 0xfff;
		data &= 0x7fff;

		newword = oldword = neogeo_paletteram16[offset];

		COMBINE_DATA(&newword);

		if (newword != oldword)
		{
			neogeo_paletteram16[offset] = newword;
			video_palette16[offset] = video_clut16[newword];

			blit_palette_mark_dirty(offset >> 4);
		}
	}
}


/*------------------------------------------------------
	Read from external memory ($e00000 - $efffff)
 -----------------------------------------------------*/

READ16_HANDLER( neogeo_externalmem_16_r )
{
	switch (exmem[exmem_counter])
	{
	case EXMEM_OBJ:
		offset  = (offset << 1) + (exmem_bank[EXMEM_OBJ] << 20);
		return (neogeo_spr_memory[offset] << 8) | neogeo_spr_memory[offset + 1];

	case EXMEM_PCMA:
		offset  = offset + (exmem_bank[EXMEM_PCMA] << 19);
		return neogeo_pcm_memory[offset] | 0xff00;

	case EXMEM_Z80:
		if (z80_cdda_offset)
		{
			if (offset == z80_cdda_offset || offset == z80_cdda_offset + 1)
				return 0;
		}
		return neogeo_z80_memory[offset] | 0xff00;

	case EXMEM_FIX:
		return neogeo_fix_memory[offset] | 0xff00;
	}

	return 0xffff;
}


/*------------------------------------------------------
	Write to external memory ($e00000 - $efffff)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_externalmem_16_w )
{
	switch (exmem[exmem_counter])
	{
	case EXMEM_OBJ:
		offset = (offset << 1) + (exmem_bank[EXMEM_OBJ] << 20);
		data = (data << 8) | (data >> 8);
		COMBINE_SWABDATA((u16 *)(neogeo_spr_memory + offset));
		if ((offset & 0x7f) == 0x7e)
			neogeo_decode_spr(neogeo_spr_memory, (offset & ~0x7f), 128);
		break;

	case EXMEM_PCMA:
		offset += exmem_bank[EXMEM_PCMA] << 19;
		neogeo_pcm_memory[offset] = data & 0xff;
		break;

	case EXMEM_Z80:
		if (z80_cdda_offset)
		{
			if (offset == z80_cdda_offset || offset == z80_cdda_offset + 1)
				return;
		}
		neogeo_z80_memory[offset] = data & 0xff;
		break;

	case EXMEM_FIX:
		neogeo_fix_memory[offset] = data & 0xff;
		if ((offset & 0x1f) == 0x1f)
			neogeo_decode_fix(neogeo_fix_memory, (offset & ~0x1f) << 1, 32);
		break;
	}
}


/*------------------------------------------------------
	Read hadware control data ($ff0000 - $ff0fff)
 -----------------------------------------------------*/

READ16_HANDLER( neogeo_hardcontrol_16_r )
{
	u16 *mem = (u16 *)memory_region_hctrl;

	offset &= 0xff;
	return mem[offset];
}


/*------------------------------------------------------
	Write hardware control data ($ff0000 - $ff0fff)
 -----------------------------------------------------*/

WRITE16_HANDLER( neogeo_hardcontrol_16_w )
{
	u16 *mem = (u16 *)memory_region_hctrl;
	offset &= 0xff;

	switch (offset << 1)
	{
	case 0x00: break; // unknown
	case 0x02: break; // unknown, CD-ROM control ?
	case 0x04: break; // unknown, CD-ROM control ?
	case 0x06: break; // unknown

	case 0x60: hardware_upload_16_w(0, data, mem_mask); break;
	case 0x64: upload_offset1_16_w(0, data, mem_mask); break;
	case 0x66: upload_offset1_16_w(1, data, mem_mask); break;
	case 0x68: upload_offset2_16_w(0, data, mem_mask); break;
	case 0x6a: upload_offset2_16_w(1, data, mem_mask); break;
	case 0x6c: upload_pattern_16_w(0, data, mem_mask); break;
	case 0x70: upload_length_16_w(0, data, mem_mask); break;
	case 0x72: upload_length_16_w(1, data, mem_mask); break;

	case 0x7e: break;	// unknown, Hardware upload settings ?
	case 0x80: break;	// unknown, Hardware upload settings ?
	case 0x82: break;	// unknown, Hardware upload settings ?
	case 0x84: break;	// unknown, Hardware upload settings ?
	case 0x86: break;	// unknown, Hardware upload settings ?
	case 0x88: break;	// unknown, Hardware upload settings ?
	case 0x8a: break;	// unknown, Hardware upload settings ?
	case 0x8c: break;	// unknown, Hardware upload settings ?
	case 0x8e: break;	// unknown, Hardware upload settings ?

	case 0x104: exmem_type_16_w(0, data, mem_mask); break;

	case 0x110: spr_disable_16_w(0, data, mem_mask); break;
	case 0x114: fix_disable_16_w(0, data, mem_mask); break;
	case 0x118: video_enable_16_w(0, data, mem_mask); break;
	case 0x11c: break;	// machine settings (read only)

	case 0x120:	// OBJ RAM latch
	case 0x122: // PCM-A RAM latch
	case 0x126: // Z80 RAM latch
	case 0x128: // FIX RAM latch
		 exmem_latch_16_w(offset, data, mem_mask);
		 break;

	case 0x140:	// OBJ RAM latch clear
	case 0x142:	// PCM-A RAM latch clear
	case 0x146:	// Z80 RAM latch clear
	case 0x148:	// FIX RAM latch clear
		 exmem_latch_clear_16_w(offset, data, mem_mask);
		 break;

	case 0x180: break;	// unknown
	case 0x182: z80_enable_16_w(0, data, mem_mask); break;

	case 0x1a0:	// OBJ RAM bank
	case 0x1a6: // unknown (PAT bank?)
		exmem_bank_16_w(offset, data, mem_mask);
		break;

	case 0x1a2: // PCM-A RAM bank
		exmem_bank_16_w(offset, data, mem_mask);
#ifdef SAVE_STATE
//		state_pcm_mark_upload();
#endif
		break;

	default:
		break;
	}

	COMBINE_DATA(&mem[offset]);
}


/****************************************************************************
	Z80 port access
 ***************************************************************************/

/*------------------------------------------------------
	Read Z80 port
 -----------------------------------------------------*/

u32 neogeo_z80_port_r(u32 port)
{
	switch (port & 0xff)
	{
	case 0x00:
		pending_command = 0;
		return sound_code;

	case 0x04:
		return YM2610_status_port_A_r(0);

	case 0x05:
		return YM2610_read_port_r(0);

	case 0x06:
		return YM2610_status_port_B_r(0);

	case 0x08:
	case 0x09:
	case 0x0a:
	case 0x0b:
		// set bank
		return 0;

	default:
		break;
	};

	return 0;
}


/*------------------------------------------------------
	Write Z80 port
 -----------------------------------------------------*/

void neogeo_z80_port_w(u32 port, u32 data)
{
	data &= 0xff;

	switch (port & 0xff)
	{
	case 0x00:	// same as 0x0c
		break;

	case 0x04:
		YM2610_control_port_A_w(0, data);
		break;

	case 0x05:
		YM2610_data_port_A_w(0, data);
		break;

	case 0x06:
		YM2610_control_port_B_w(0, data);
		break;

	case 0x07:
		YM2610_data_port_B_w(0, data);
		break;

	case 0x08:
		/* NMI enable / acknowledge? (the data written doesn't matter) */
		break;

	case 0x0c:
		result_code = data;
		break;

	case 0x18:
		/* NMI disable? (the data written doesn't matter) */
		break;

	case 0x80:
//		cdda_stop();
		break;

	case 0xc0:
	case 0xc1:
	case 0xc2:
		break;

	default:
		break;
	}
}


/****************************************************************************
	Sound IRQ handler
 ***************************************************************************/

void neogeo_sound_irq(int irq)
{
	if (neogeo_sound_enable)
       	z80_set_irq_line(0, irq ? ASSERT_LINE : CLEAR_LINE);
}


/****************************************************************************
	Save / Load state
 ***************************************************************************/

#ifdef SAVE_STATE
void state_save_driver(u8 *state_buf)
{
	u8 *in = state_buf;

	state_save_long(&irq1start, 1);
	state_save_long(&irq1control, 1);
	state_save_long(&irq1pos_value, 1);
	state_save_long(&vblank_int, 1);
	state_save_long(&scanline_int, 1);
	state_save_long(&scanline_read, 1);
	state_save_long(&frame_counter, 1);
	state_save_long(&sound_code, 1);
	state_save_long(&result_code, 1);
	state_save_long(&pending_command, 1);
	state_save_long(&neogeo_frame_counter, 1);
	state_save_long(&neogeo_frame_counter_speed, 1);
	state_save_byte(memory_region_hctrl, 0x200);
}


void state_load_driver(u8 *state_buf)
{
	u8 *out = state_buf;

	state_load_long(&irq1start, 1);
	state_load_long(&irq1control, 1);
	state_load_long(&irq1pos_value, 1);
	state_load_long(&vblank_int, 1);
	state_load_long(&scanline_int, 1);
	state_load_long(&scanline_read, 1);
	state_load_long(&frame_counter, 1);
	state_load_long(&sound_code, 1);
	state_load_long(&result_code, 1);
	state_load_long(&pending_command, 1);
	state_load_long(&neogeo_frame_counter, 1);
	state_load_long(&neogeo_frame_counter_speed, 1);
	state_load_byte(memory_region_hctrl, 0x200);

	current_rasterline = 0;
	current_rastercounter = RASTER_COUNTER_START;
	watchdog_counter = 3 * FPS;

	memset(exmem, 0, sizeof(exmem));
	memset(exmem_latch, 0, sizeof(exmem_latch));
	memset(exmem_bank, 0, sizeof(exmem_bank));
	exmem_counter = 0;

	upload_mode = UPLOAD_IMMIDIATE;
	upload_type = UNKNOWN_TYPE;
	upload_offset1 = 0;
	upload_offset2 = 0;
	upload_length = 0;
	upload_pattern = 0;
	upload_executing = 0;
}

#endif


/****************************************************************************
	Driver structure
 ***************************************************************************/

const GAMES games[100] =
{
	{ "nam1975",  0x0001, NORMAL },
	{ "bstars",   0x0002, NORMAL },
	{ "tpgolf",   0x0003, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "mahretsu", 0x0004, NORMAL },
	{ "ridhero",  0x0006, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "alpham2",  0x0007, NORMAL },
	{ "cyberlip", 0x0010, NORMAL },
	{ "superspy", 0x0011, NORMAL },
	{ "mutnat",   0x0014, NORMAL },
	{ "sengoku",  0x0017, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "burningf", 0x0018, NORMAL },
	{ "lbowling", 0x0019, NORMAL },
	{ "gpilots",  0x0020, NORMAL },
	{ "joyjoy",   0x0021, NORMAL },
	{ "lresort",  0x0024, NORMAL },
	{ "2020bb",   0x0030, NORMAL },
	{ "socbrawl", 0x0031, NORMAL },
	{ "roboarmy", 0x0032, NORMAL },
	{ "fatfury",  0x0033, NORMAL },
	{ "fbfrenzy", 0x0034, NORMAL },
	{ "kotm2",    0x0039, NORMAL },
	{ "sengoku2", 0x0040, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "bstars2",  0x0041, NORMAL },
	{ "3countb",  0x0043, NORMAL },
	{ "aof",      0x0044, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "samsho",   0x0045, NORMAL },
	{ "tophuntr", 0x0046, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "fatfury2", 0x0047, NORMAL },
	{ "ssideki",  0x0052, NORMAL },
	{ "kof94",    0x0055, NORMAL },
	{ "aof2",     0x0056, AOF2   },
	{ "fatfursp", 0x0058, NORMAL },
	{ "savagere", 0x0059, NORMAL },
	{ "ssideki2", 0x0061, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "samsho2",  0x0063, NORMAL },
	{ "pspikes2", 0x0068, NORMAL },
	{ "fatfury3", 0x069c, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "sonicwi2", 0x0075, NORMAL },
	{ "galaxyfg", 0x0078, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "quizkof",  0x0080, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "ssideki3", 0x0081, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "doubledr", 0x0082, NORMAL },
	{ "pbobblen", 0x0083, NORMAL },
	{ "kof95",    0x0084, NORMAL },
	{ "ssrpg",    0x0085, NORMAL },
	{ "samsho3",  0x0087, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "stakwin",  0x0088, NORMAL },
	{ "pulstar",  0x0089, NORMAL },
	{ "kabukikl", 0x0092, NORMAL },
	{ "gowcaizr", 0x0094, NORMAL },
	{ "rbff1",    0x0095, NORMAL },
	{ "aof3",     0x0096, NORMAL },
	{ "sonicwi3", 0x0097, NORMAL },
	{ "fromanc2", 0x0098, NORMAL },
	{ "turfmast", 0x0200, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "mslug",    0x0201, NORMAL },
	{ "ngcdsp",   0x0205, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "neodrift", 0x0213, CHECK_RASTERCOUNTER },
	{ "kof96",    0x0214, NORMAL },
	{ "ragnagrd", 0x0218, NORMAL },
	{ "pgoal",    0x0219, NORMAL },
	{ "ironclad", 0x0220, NORMAL },
	{ "magdrop2", 0x0221, NORMAL },
	{ "samsho4",  0x0222, NORMAL },
	{ "kof96ngc", 0x0229, NORMAL },
	{ "rbffspec", 0x0223, NORMAL },
	{ "breakers", 0x0230, NORMAL },
	{ "kof97",    0x0232, NORMAL },
	{ "lastblad", 0x0234, NORMAL },
	{ "rbff2",    0x0240, NORMAL },
	{ "mslug2",   0x0241, NORMAL },
	{ "kof98",    0x0242, NORMAL },
	{ "lastbld2", 0x0243, NORMAL },
	{ "kof99",    0x0251, NORMAL },

	{ "bjourney", 0x0022, NORMAL },
	{ "crswords", 0x0037, NORMAL },
	{ "rallych",  0x0038, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "ncommand", 0x0050, NORMAL },
	{ "wh1",      0x0053, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "wh2",      0x0057, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "wh2j",     0x0064, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "aodk",     0x0074, NORMAL },
	{ "whp",      0x0090, NORMAL },
	{ "mosyougi", 0x0203, CHECK_RASTERCOUNTER /* BUSY */ },
	{ "overtop",  0x0212, CHECK_RASTERCOUNTER },
	{ "ninjamas", 0x0217, NORMAL },
	{ "twinspri", 0x0224, NORMAL },
	{ "maglord",  0x0005, NORMAL },
	{ "ncombat",  0x0009, NORMAL },
	{ "crsword2", 0x0054, NORMAL },
	{ "adkworld", 0x0204, NORMAL },
	{ "zintrick", 0x0211, NORMAL },

	{ "janshin",  0x0048, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "viewpoin", 0x0051, CHECK_RASTERCOUNTER /* RASTER */ },

	{ "wjammers", 0x0065, NORMAL },
	{ "karnovr",  0x0066, CHECK_RASTERCOUNTER /* RASTER */ },
	{ "strhoop",  0x0079, NORMAL },

	{ "default",  0x0000, NORMAL },
	{ "default",  0x0000, NORMAL },
	{ "neogeocd", 0x0000, NORMAL }
};
