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

Eolith Ghost Hardware driver

1. CPU
- 16/32bit RISC Microprocessor(ARM920T CPU core)
- Separate 16KB Instruction and 16KB Data Cache
- Operating Frequency : Up to 203MHz

2. MEMORY
- Main Memory : 32Mbytes(Maximum 128Mbytes) SDRAM
- NAND FLASH : 16M,32M, 64M, 128M, 256Mbytes

3. VIDEO
- RGB (5:6:5) format
- Supports 1,2,4 or 8 bpp(bit-per-pixel) palette color displays
- Supports 16 bpp non-palette true-color displays
- Supports multiple screen size
Typical actual screen size : 320*240, 640*480
Maximum virtual screen size is 4Mbytes
Maximum virtual screen size in 64K color mode : 2048*1024
- Rendering Performance : 480 Objects per sec.(256*256 sprite)

4. SOUND
Internal Audio Engine or External System Available
TDA1519(Philips)Stereo Power AMP
- Internal Audio Engine : 1-ch IIS-bus for audio interface
Serial, 8-/16-bit per channel data transfers
128 Bytes (64-Byte + 64-Byte) FIFO for Tx/Rx
- External System(Option) : General MIDI Chipset QDSP1000
MIDI 16th Channel(32 Poly) using as Effect
Effect EPROM : 512Kbyte or 1Mbyte
MIDI Background Music EPROM:512Kbyte

5. Board Feature
- JAMMA Standard ( Application of PACK TYPE ) : 1P,2P ? Joystick (Up, Down, Left, Right), 4 Push Buttons, Start Button
- 1P,2P ? Coin, Coin Counter
- On Board Service Button, On Board Test Button
- Photo Gun : 1P, 2P Photo Gun (or Light Pen) (Option)
- ETC : Security System Available
Multi I/O Port Available
Coin Counter Available
Gun Shooting Effect Available (Option)
Hopper, Ticket Counter, Prize System (Option)

6. Develoment
- On-chip ICEbreaker debug support with JTAG-based debugging solution
- MultiICE, OPENICE etc.
- Compiler : ADS, SDT


*/

#include "emu.h"
#include "cpu/arm7/arm7.h"
#include "cpu/arm7/arm7core.h"
#include "machine/s3c2410.h"
//#include "machine/smartmed.h"
#include "machine/i2cmem.h"


enum nand_mode_t
{
	NAND_M_INIT,		// initial state
	NAND_M_READ,		// read page data
};

struct nand_t
{
	nand_mode_t mode;
	int page_addr;
	int byte_addr;
	int addr_load_ptr;
};


class ghosteo_state : public driver_device
{
public:
	ghosteo_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag) { }

	UINT32 *m_system_memory;
	UINT32 *m_steppingstone;
	int m_security_count;
	UINT32 m_bballoon_port[20];
	struct nand_t m_nand;
};



/*
Power management:
Normal, Idle, Slow & Power-off
4-ch 16 bit PWM ( Pulse Width Modulation), & 1-ch 16-bit timer for OS
RTC : 32.768 KHz, alarm interrupt
GPIO :117 (multiplexed I/O)
3-ch UARTs
4 ch DMA Controllers
8-ch 10-bit A/D (Max. 500KSPS), including TSP Controller
TFT LCD/STN LCD Controller (16bit, 640?480 maximum)
16-bit Watch-dog Timer
1-ch IIC-Bus Interface
IIS-Bus Interface
Screen Size: up to 640 x 480
2-ch SPI (Synchronous Serial I/O)
SD Host/MMC (Multi Media Card) Interface
USB Host/Device Interface
Debug & TEST
NAND Flash Controller (4KB internal buffer)
24-ch external interrupts Controller (Wake-up source 16-ch)
*/

static const UINT8 security_data[] = { 0x01, 0xC4, 0xFF, 0x22 };


static UINT32 s3c2410_gpio_port_r( device_t *device, int port)
{
	ghosteo_state *state = device->machine().driver_data<ghosteo_state>();
	UINT32 data = state->m_bballoon_port[port];
	switch (port)
	{
		case S3C2410_GPIO_PORT_F :
		{
			data = (data & ~0xFF) | security_data[state->m_security_count]; // bballoon security @ 0x3001BD68
		}
		break;
		case S3C2410_GPIO_PORT_G :
		{
			data = data ^ 0x20;
			state->m_bballoon_port[port] = data;
		}
		break;
	}
	return data;
}

static void s3c2410_gpio_port_w( device_t *device, int port, UINT32 data)
{
	ghosteo_state *state = device->machine().driver_data<ghosteo_state>();
	UINT32 old_value = state->m_bballoon_port[port];
	state->m_bballoon_port[port] = data;
	switch (port)
	{
		case S3C2410_GPIO_PORT_F :
		{
			switch (data)
			{
				case 0x04 : state->m_security_count = 0; break;
				case 0x44 : state->m_security_count = 2; break;
			}
		}
		break;
		case S3C2410_GPIO_PORT_G :
		{
			// 0 -> 1
			if (((data & 0x10) != 0) && ((old_value & 0x10) == 0))
			{
				logerror( "security_count %d -> %d\n", state->m_security_count, state->m_security_count + 1);
				state->m_security_count++;
				if (state->m_security_count > 7) state->m_security_count = 0;
			}
		}
		break;
	}
}

static WRITE8_DEVICE_HANDLER( s3c2410_nand_command_w )
{
	ghosteo_state *state = device->machine().driver_data<ghosteo_state>();
	struct nand_t &nand = state->m_nand;
//  device_t *nand = device->machine().device( "nand");
	logerror( "s3c2410_nand_command_w %02X\n", data);
	switch (data)
	{
		case 0xFF :
		{
			nand.mode = NAND_M_INIT;
			s3c2410_pin_frnb_w( device, 1);
		}
		break;
		case 0x00 :
		{
			nand.mode = NAND_M_READ;
			nand.page_addr = 0;
			nand.addr_load_ptr = 0;
		}
		break;
	}
}

static WRITE8_DEVICE_HANDLER( s3c2410_nand_address_w )
{
	ghosteo_state *state = device->machine().driver_data<ghosteo_state>();
	struct nand_t &nand = state->m_nand;
//  device_t *nand = device->machine().device( "nand");
	logerror( "s3c2410_nand_address_w %02X\n", data);
	switch (nand.mode)
	{
		case NAND_M_INIT :
		{
			logerror( "nand: unexpected address port write\n");
		}
		break;
		case NAND_M_READ :
		{
			if (nand.addr_load_ptr == 0)
			{
				nand.byte_addr = data;
			}
			else
			{
				nand.page_addr = (nand.page_addr & ~(0xFF << ((nand.addr_load_ptr - 1) * 8))) | (data << ((nand.addr_load_ptr - 1) * 8));
			}
			nand.addr_load_ptr++;
			if ((nand.mode == NAND_M_READ) && (nand.addr_load_ptr == 4))
			{
				s3c2410_pin_frnb_w( device, 0);
				s3c2410_pin_frnb_w( device, 1);
			}
		}
		break;
	}
}

static READ8_DEVICE_HANDLER( s3c2410_nand_data_r )
{
	ghosteo_state *state = device->machine().driver_data<ghosteo_state>();
	struct nand_t &nand = state->m_nand;
//  device_t *nand = device->machine().device( "nand");
	UINT8 data = 0;
	switch (nand.mode)
	{
		case NAND_M_INIT :
		{
			logerror( "nand: unexpected address port read\n");
		}
		break;
		case NAND_M_READ :
		{
			UINT8 *flash = (UINT8 *)device->machine().region( "user1")->base();
			if (nand.byte_addr < 0x200)
			{
				data = *(flash + nand.page_addr * 0x200 + nand.byte_addr);
			}
			else
			{
				if ((nand.byte_addr >= 0x200) && (nand.byte_addr < 0x204))
				{
					UINT8 mecc[4];
					s3c2410_nand_calculate_mecc( flash + nand.page_addr * 0x200, 0x200, mecc);
					data = mecc[nand.byte_addr-0x200];
				}
				else
				{
					data = 0xFF;
				}
			}
			nand.byte_addr++;
			if (nand.byte_addr == 0x210)
			{
				nand.byte_addr = 0;
				nand.page_addr++;
				if (nand.page_addr == 0x10000) nand.page_addr = 0;
			}
		}
		break;
	}
	logerror( "s3c2410_nand_data_r %02X\n", data);
	return data;
}

static WRITE8_DEVICE_HANDLER( s3c2410_nand_data_w )
{
//  device_t *nand = device->machine().device( "nand");
	logerror( "s3c2410_nand_data_w %02X\n", data);
}

static WRITE_LINE_DEVICE_HANDLER( s3c2410_i2c_scl_w )
{
	device_t *i2cmem = device->machine().device( "i2cmem");
//  logerror( "s3c2410_i2c_scl_w %d\n", state ? 1 : 0);
	i2cmem_scl_write( i2cmem, state);
}

static READ_LINE_DEVICE_HANDLER( s3c2410_i2c_sda_r )
{
	device_t *i2cmem = device->machine().device( "i2cmem");
	int state;
	state = i2cmem_sda_read( i2cmem);
//  logerror( "s3c2410_i2c_sda_r %d\n", state ? 1 : 0);
	return state;
}

static WRITE_LINE_DEVICE_HANDLER( s3c2410_i2c_sda_w )
{
	device_t *i2cmem = device->machine().device( "i2cmem");
//  logerror( "s3c2410_i2c_sda_w %d\n", state ? 1 : 0);
	i2cmem_sda_write( i2cmem, state);
}

static WRITE32_HANDLER( sound_w )
{
	if ((data >= 0x20) && (data <= 0x7F))
	{
		logerror( "sound_w: music %d\n", data - 0x20);
	}
	else if ((data >= 0x80) && (data <= 0xFF))
	{
		logerror( "sound_w: effect %d\n", data - 0x80);
	}
	else
	{
		logerror( "sound_w: unknown (%d)\n", data);
	}
}

static ADDRESS_MAP_START( bballoon_map, AS_PROGRAM, 32 )
	AM_RANGE(0x00000000, 0x00000fff) AM_RAM AM_BASE_MEMBER(ghosteo_state, m_steppingstone) AM_MIRROR(0x40000000)
	AM_RANGE(0x10000000, 0x10000003) AM_READ_PORT("10000000")
	AM_RANGE(0x10100000, 0x10100003) AM_READ_PORT("10100000")
	AM_RANGE(0x10200000, 0x10200003) AM_READ_PORT("10200000")
	AM_RANGE(0x10300000, 0x10300003) AM_WRITE(sound_w)
	AM_RANGE(0x30000000, 0x31ffffff) AM_RAM AM_BASE_MEMBER(ghosteo_state, m_system_memory) AM_MIRROR(0x02000000)
ADDRESS_MAP_END

/*
static INPUT_PORTS_START( bballoon )
    PORT_START("10000000")
    PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
    PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
    PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
    PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
    PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
    PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
    PORT_BIT( 0xFFFFFFC0, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1)
    PORT_START("10100000")
    PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
    PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
    PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
    PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
    PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
    PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
    PORT_BIT( 0xFFFFFFC0, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(2)
    PORT_START("10200000")
    PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_COIN1 )
    PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_COIN2 )
    PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_START1 )
    PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_START2 )
    PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_START3 )
    PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_SERVICE1 ) // "test button"
    PORT_BIT( 0x00000040, IP_ACTIVE_LOW, IPT_START4 )
    PORT_BIT( 0x00000080, IP_ACTIVE_LOW, IPT_SERVICE2 ) // "service button"
    PORT_BIT( 0xFFFFFF00, IP_ACTIVE_LOW, IPT_START5 )
INPUT_PORTS_END
*/

static INPUT_PORTS_START( bballoon )
	PORT_START("10000000")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1)
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1)
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1)
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1)
	PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0xFFFFFFC0, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START("10100000")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2)
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2)
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2)
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2)
	PORT_BIT( 0x00000010, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0xFFFFFFC0, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_START("10200000")
	PORT_BIT( 0x00000001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x00000002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x00000004, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x00000008, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x00000020, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x00000080, IP_ACTIVE_LOW, IPT_SERVICE2 )
	PORT_BIT( 0xFFFFFF50, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

/*
static NAND_INTERFACE( bballoon_nand_intf )
{
    DEVCB_DEVICE_LINE("s3c2410", s3c2410_pin_frnb_w)
};
*/

static const s3c2410_interface bballoon_s3c2410_intf =
{
	// GPIO (port read / port write)
	{ s3c2410_gpio_port_r, s3c2410_gpio_port_w },
	// I2C (scl write / sda read / sda write)
	{ s3c2410_i2c_scl_w, s3c2410_i2c_sda_r, s3c2410_i2c_sda_w },
	// ADC (data read)
	{ NULL },
	// I2S (data write)
	{ NULL },
	// NAND (command write, address write, data read, data write)
	{ s3c2410_nand_command_w, s3c2410_nand_address_w, s3c2410_nand_data_r, s3c2410_nand_data_w }
};

static const i2cmem_interface i2cmem_interface =
{
	I2CMEM_SLAVE_ADDRESS, 0, 256
};


device_t* s3c2410;

static READ32_HANDLER( bballoon_speedup_r )
{
	UINT32 ret = s3c2410_lcd_r(s3c2410, offset+0x10/4, mem_mask);


	int pc = cpu_get_pc(&space->device());

	// these are vblank waits
	if (pc == 0x3001c0e4 || pc == 0x3001c0d8)
	{
		// BnB Arcade
		device_spin_until_time(&space->device(), attotime::from_usec(20));
	}
	else if (pc == 0x3002b580 || pc == 0x3002b550)
	{
		// Happy Tour
		device_spin_until_time(&space->device(), attotime::from_usec(20));
	}
	//else
	//  printf("speedup %08x %08x\n", pc, ret);

	return ret;
}

static MACHINE_RESET( bballoon )
{
	machine.device("maincpu")->memory().space(AS_PROGRAM)->install_legacy_read_handler(0x4d000010, 0x4d000013, FUNC(bballoon_speedup_r));
	s3c2410 = machine.device("s3c2410");
}

static MACHINE_CONFIG_START( bballoon, ghosteo_state )

	/* basic machine hardware */
	MCFG_CPU_ADD("maincpu", ARM9, 200000000)
	MCFG_CPU_PROGRAM_MAP(bballoon_map)

	MCFG_SCREEN_ADD("screen", RASTER)
	MCFG_SCREEN_REFRESH_RATE(60)
	MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500) /* not accurate */)
	MCFG_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
	MCFG_SCREEN_SIZE(455, 262)
	MCFG_SCREEN_VISIBLE_AREA(0, 320-1, 0, 256-1)
	MCFG_SCREEN_UPDATE(s3c2410)

	MCFG_PALETTE_LENGTH(256)

	MCFG_MACHINE_RESET( bballoon )

	MCFG_VIDEO_START(s3c2410)

	MCFG_S3C2410_ADD("s3c2410", 12000000, bballoon_s3c2410_intf)

//  MCFG_NAND_ADD("nand", 0xEC, 0x75)
//  MCFG_DEVICE_CONFIG(bballoon_nand_intf)

//  MCFG_I2CMEM_ADD("i2cmem", 0xA0, 0, 0x100, NULL)
	MCFG_I2CMEM_ADD("i2cmem", i2cmem_interface)

	/* sound hardware */
MACHINE_CONFIG_END

/*
Balloon & Balloon
Eolith, 2003

PCB Layout
----------

GHOST Ver1.1 2003.03.28
|---------------------------------------------|
|TDA1519A              B2.U20  LED      RESET |
|         QS1000                              |
|DA1311A            24MHz                  LED|
|                                             |
|                                             |
|                  |---------|                |
|   QS1001A.U17    |ARM 312  |                |
|J                 |SAMSUNG  |                |
|A       B1.U16    |S3C2410  |   IC42S16800   |
|M                 |         |                |
|M                 |---------|                |
|A                                            |
|                                IC42S16800   |
|                            12MHz            |
|                    M24CL16-S                |
|                                             |
|                                             |
|                             *       FLASH.U1|
|      SERVICE                                |
|DSW1  TEST                                   |
|---------------------------------------------|
Notes:
      *          - Unknown SOIC20 chip, surface scratched
      FLASH.U1   - Samsung K9F5608U0B 256MBit NAND FlashROM (TSOP48)
      U16        - 27C801 8MBit EPROM
      U20        - 29F040 EEPROM
      IC42S16800 - ISSI IC42S16800 4(2)M x 8(16) Bits x 4 Banks (128MBit) SDRAM

      QS1000     - QDSP QS1000 AdMOS 9638R, Wavetable Audio chip, clock input of 24.000MHz (QFP100)
      QS1001A    - QDSP QS1001A 512k x8 MaskROM (SOP32)

      qs1001a.u17 was not dumped from this PCB, but is a standard sample rom found on many Eolith games
                  see eolith.c and vegaeo.c drivers
*/

// The NAND dumps are missing the ECC data.  We calculate it on the fly, because the games require it, but really it should be dumped hence the 'BAD DUMP' flags
ROM_START( bballoon )
	ROM_REGION( 0x2000000, "user1", 0 ) /* ARM 32 bit code */
	ROM_LOAD( "flash.u1",     0x000000, 0x2000000, BAD_DUMP CRC(73285634) SHA1(4d0210c1bebdf3113a99978ffbcd77d6ee854168) ) // missing ECC data

	// banked every 0x10000 bytes ?
	ROM_REGION( 0x080000, "user2", 0 )
	ROM_LOAD( "b2.u20",       0x000000, 0x080000, CRC(0a12334c) SHA1(535b5b34f28435517218100d70147d87809f485a) )

	ROM_REGION( 0x100000, "sfx", 0 ) /* QDSP samples (SFX) */
	ROM_LOAD( "b1.u16",       0x000000, 0x100000, CRC(c42c1c85) SHA1(e1f49d556ffd6bc27142a7784c3bb8e37999857d) )

	ROM_REGION( 0x080000, "wavetable", 0 ) /* QDSP wavetable rom */
	ROM_LOAD( "qs1001a.u17",  0x000000, 0x80000, CRC(d13c6407) SHA1(57b14f97c7d4f9b5d9745d3571a0b7115fbe3176) )
ROM_END

ROM_START( hapytour ) /* Same hardware: GHOST Ver1.1 2003.03.28 */
	ROM_REGION( 0x2000000, "user1", 0 ) /* ARM 32 bit code */
	ROM_LOAD( "flash.u1",     0x000000, 0x2000000, BAD_DUMP CRC(49deb7f9) SHA1(708a27d7177cf6261a49ded975c2bbb6c2427742) ) // missing ECC data

	// banked every 0x10000 bytes ?
	ROM_REGION( 0x080000, "user2", 0 )
	ROM_LOAD( "ht.u20",       0x000000, 0x080000, CRC(c0581fce) SHA1(dafce679002534ffabed249a92e6b83301b8312b) )

	ROM_REGION( 0x100000, "sfx", 0 ) /* QDSP samples (SFX) */
	ROM_LOAD( "ht.u16",       0x000000, 0x100000, CRC(6a590a3a) SHA1(c1140f70c919661162334db66c6aa0ad656bfc47) )

	ROM_REGION( 0x080000, "wavetable", 0 ) /* QDSP wavetable rom */
	ROM_LOAD( "qs1001a.u17",  0x000000, 0x80000, CRC(d13c6407) SHA1(57b14f97c7d4f9b5d9745d3571a0b7115fbe3176) )
ROM_END

static DRIVER_INIT( bballoon )
{
	ghosteo_state *state = machine.driver_data<ghosteo_state>();
	memcpy( state->m_steppingstone, machine.region( "user1")->base(), 4 * 1024);
}

GAME( 2003, bballoon, 0, bballoon, bballoon, bballoon, ROT0, "Eolith", "BnB Arcade", GAME_NO_SOUND )
GAME( 2005, hapytour, 0, bballoon, bballoon, bballoon, ROT0, "GAV Company", "Happy Tour", GAME_NO_SOUND )
