#include "taitoio.h"
#include "taitosnd.h"
#include "tc0180vcu.h"

#include "cpu/z80/z80.h"
#include "sound/ymopn.h"
#include "speaker.h"
#include "emupal.h"
#include "screen.h"

namespace {

class comet02_96b_state : public driver_device
{
public:
	comet02_96b_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_main_cpu(*this, "maincpu")
		, m_audio_cpu(*this, "audiocpu")
		, m_tc0180vcu(*this, "tc0180vcu")
		, m_screen(*this, "screen")
		, m_palette(*this, "palette")
		, m_rom(*this, DEVICE_SELF)
	{
	}

	u8 rom_r(offs_t offset) { return m_rom[offset & (m_rom.length() - 1)]; }

	u8 vram_bank_r(offs_t offset) { return m_vram_bank; }
	void vram_bank_w(offs_t offset, u8 data) { m_vram_bank = data; }
	u8 rom_bank_r() { return m_rom_bank; }
	void rom_bank_w(u8 data) { m_rom_bank = data; }
	u8 check_window() { return dest; }
	void set_window(u8 data) { dest = data & 1; } // Bit 0 sets mode (0=VRAM, 1=palette)

	void coin_control_w(u8 data);

	u8 window_r(offs_t offset);
	void window_w(offs_t offset, u8 data);

	void comet02_96b(machine_config &config) ATTR_COLD;

protected:
	virtual void machine_start() override ATTR_COLD;
	virtual void machine_reset() override ATTR_COLD;

	uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);

	void main_map(address_map &map) ATTR_COLD;
	void sub_map(address_map &map) ATTR_COLD;
	void sound_map(address_map &map) ATTR_COLD;

	u8 m_vram_bank;
	u8 m_rom_bank;
	u8 dest;

	required_device<cpu_device> m_main_cpu;
	required_device<cpu_device> m_audio_cpu;
	required_device<tc0180vcu_device> m_tc0180vcu;
	required_device<screen_device> m_screen;
	required_device<palette_device> m_palette;
	required_region_ptr<u8> m_rom;
};

void comet02_96b_state::machine_start()
{
    	// rom_r assumes it can make a mask with (m_rom.length() - 1)
	assert(!(m_rom.length() & (m_rom.length() - 1)));

	save_item(NAME(m_vram_bank));
	save_item(NAME(m_rom_bank));
}

void comet02_96b_state::machine_reset()
{
	m_vram_bank = 0;
	m_rom_bank = 0;
}

uint32_t comet02_96b_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
	uint8_t const video_control = m_tc0180vcu->get_videoctrl();

	if (BIT(~video_control, 5))
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	//Draw playfields
	m_tc0180vcu->tilemap_draw(screen, bitmap, cliprect, 0, 1);
	m_tc0180vcu->draw_framebuffer(bitmap, cliprect, 1);
	m_tc0180vcu->tilemap_draw(screen, bitmap, cliprect, 1, 0);
	m_tc0180vcu->draw_framebuffer(bitmap, cliprect, 0);
	m_tc0180vcu->tilemap_draw(screen, bitmap, cliprect, 2, 0);

	return 0;
}

void comet02_96b_state::coin_control_w(u8 data)
{
	machine().bookkeeping().coin_lockout_w(0, ~data & 0x01);
	machine().bookkeeping().coin_lockout_w(1, ~data & 0x02);
	machine().bookkeeping().coin_counter_w(0, data & 0x04);
	machine().bookkeeping().coin_counter_w(1, data & 0x08);
}

// Handlers for switchable window in 0xc000-0xfdff (VRAM or palette)
u8 comet02_96b_state::window_r(offs_t offset)
{
    //if(dest==0)		return обратиться по адресу карты tc0180vcu_memrw((m_vram_bank*0x3e00+offset)&0x7ffff);
    else			return m_palette[(m_vram_bank*0x3e00+offset)&0x1fff]&0xff;
}

void comet02_96b_state::window_w(offs_t offset, u8 data)
{
    //if (dest==0)		return обратиться по адресу карты tc0180vcu_memrw((m_vram_bank*0x3e00+offset)&0x7ffff, data);
    else			m_palette[(m_vram_bank*0x3e00+offset)&0x1fff] = data;
}


void comet02_96b_state::main_map(address_map &map)
{
	map(0x0000, 0x5fff).r(FUNC(rom_r));
	map(0x6000, 0x7fff).lr8(NAME([this] (offs_t offset) { return rom_r((m_rom_bank << 13) | (offset & 0x1fff)); }));
	map(0x8000, 0x9fff).ram();
	map(0xa000, 0xbfff).ram().share("share1");

	// 0xc000-0xfdff RAM banks (Connected in VRAMs, 4KB boundary)

	map(0xc000, 0xfdff).rw(FUNC(window_r), FUNC(window_w)); //m(m_tc0180vcu, FUNC(tc0180vcu_device::tc0180vcu_memrw))

	//map(0xfe00, 0xfeff).rw(m_main_cpu, FUNC(tc0090lvc_device::vregs_r), FUNC(tc0090lvc_device::vregs_w));
	map(0xff00, 0xff02).mirror(0x00f0).noprw();
	map(0xff03, 0xff03).mirror(0x00f0).rw(m_main_cpu, FUNC(check_window), FUNC(set_window));
	//map(0xff04, 0xff07).mirror(0x00f0).rw(m_main_cpu, FUNC(vram_bank_r), FUNC(vram_bank_w));
	map(0xff08, 0xff08).mirror(0x00f0).rw(m_main_cpu, FUNC(rom_bank_r), FUNC(rom_bank_w));
}

void comet02_96b_state::sub_map(address_map &map)
{
	map(0x0000, 0x7fff).rom();
	map(0x8000, 0xbfff).bankr("slavebank");
	map(0xc000, 0xdfff).ram().share("share1");
	map(0xe000, 0xe007).rw("tc0220ioc", FUNC(tc0220ioc_device::read), FUNC(tc0220ioc_device::write));
	map(0xe008, 0xe00f).nopr();
	map(0xe800, 0xe800).nopr().w("ciu", FUNC(pc060ha_device::master_port_w));
	map(0xe801, 0xe801).rw("ciu", FUNC(pc060ha_device::master_comm_r), FUNC(pc060ha_device::master_comm_w));
	//map(0xf000, 0xf000).rw(FUNC(champwr_state::slave_rombank_r), FUNC(champwr_state::slave_rombank_w));
}

void comet02_96b_state::sound_map(address_map &map)
{
	map(0x0000, 0x3fff).rom();
	//map(0x4000, 0x7fff).bankr(m_audiobank);
	map(0xc000, 0xdfff).ram();
	map(0xe000, 0xe003).rw("ymsnd", FUNC(ym2610_device::read), FUNC(ym2610_device::write));
	map(0xe200, 0xe200).nopr().w("tc0140syt", FUNC(tc0140syt_device::slave_port_w));
	map(0xe201, 0xe201).rw("tc0140syt", FUNC(tc0140syt_device::slave_comm_r), FUNC(tc0140syt_device::slave_comm_w));
	map(0xe400, 0xe403).nopw(); /* pan */
	map(0xea00, 0xea00).nopr();
	map(0xee00, 0xee00).nopw(); /* ? */
	map(0xf000, 0xf000).nopw(); /* ? */
	//map(0xf200, 0xf200).w(FUNC(comet02_96b_state::sound_bankswitch_w));   /* ?? */
}

static INPUT_PORTS_START( quakelof )
	PORT_START("DSWA")
	//TAITO_MACHINE_NO_COCKTAIL_LOC(SW1)  // all 2 in manual
	//TAITO_COINAGE_WORLD_LOC(SW1)

	PORT_START("DSWB")
	//TAITO_DIFFICULTY_LOC(SW2)
	PORT_DIPNAME( 0x0c, 0x0c, "Time" )          PORT_DIPLOCATION("SW2:3,4")
	PORT_DIPSETTING(    0x08, "2 minutes" )
	PORT_DIPSETTING(    0x0c, "3 minutes" )
	PORT_DIPSETTING(    0x04, "4 minutes" )
	PORT_DIPSETTING(    0x00, "5 minutes" )
	PORT_DIPNAME( 0x30, 0x30, "'1 minute' Lasts:" )     PORT_DIPLOCATION("SW2:5,6")
	PORT_DIPSETTING(    0x00, "30 sec" )
	PORT_DIPSETTING(    0x10, "40 sec" )
	PORT_DIPSETTING(    0x30, "50 sec" )
	PORT_DIPSETTING(    0x20, "60 sec" )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Allow_Continue ) )   PORT_DIPLOCATION("SW2:7")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPUNUSED_DIPLOC( 0x80, 0x80, "SW2:8" )        /* Listed as Unused */

	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_TILT )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SERVICE1 )

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_IMPULSE(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_IMPULSE(1)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START("IN2")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1)
INPUT_PORTS_END

void comet02_96b_state::comet02_96b(machine_config &config)
{
	Z80(config, m_main_cpu, XTAL(13'330'560)/2);
	m_main_cpu->set_addrmap(AS_PROGRAM, &comet02_96b_state::main_map);

	Z80(config, m_audio_cpu, 24000000/6);   /* 4 MHz */
	m_audio_cpu->set_addrmap(AS_PROGRAM, &comet02_96b_state::sound_map);

	z80_device &slave(Z80(config, "slave", 12_MHz_XTAL/3)); /* verified on pcb */
	slave.set_addrmap(AS_PROGRAM, &comet02_96b_state::sub_map);
	slave.set_vblank_int("screen", FUNC(comet02_96b_state::irq0_line_hold));

	config.set_perfect_quantum(m_main_cpu);

	tc0220ioc_device &tc0220ioc(TC0220IOC(config, "tc0220ioc", 0));
	tc0220ioc.read_0_callback().set_ioport("DSWA");
	tc0220ioc.read_1_callback().set_ioport("DSWB");
	tc0220ioc.read_2_callback().set_ioport("IN0");
	tc0220ioc.read_3_callback().set_ioport("IN1");
	tc0220ioc.write_4_callback().set(FUNC(comet02_96b_state::coin_control_w));
	tc0220ioc.read_7_callback().set_ioport("IN2");

	/* video hardware */
	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
	m_screen->set_refresh_hz(60);
	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(0));
	m_screen->set_size(64*8, 32*8);
	m_screen->set_visarea(0*8, 40*8-1, 2*8, 30*8-1);
	m_screen->set_screen_update(FUNC(comet02_96b_state::screen_update));
	m_screen->set_palette(m_palette);

	PALETTE(config, m_palette).set_format(palette_device::RGBx_444, 4096);

	TC0180VCU(config, m_tc0180vcu, 27.164_MHz_XTAL / 4);
	m_tc0180vcu->set_fb_colorbase(0x10);
	m_tc0180vcu->set_bg_colorbase(0x30);
	m_tc0180vcu->set_fg_colorbase(0x20);
	m_tc0180vcu->set_tx_colorbase(0x00);
	m_tc0180vcu->set_palette(m_palette);
	m_tc0180vcu->inth_callback().set_inputline(m_main_cpu, 0, HOLD_LINE);
	m_tc0180vcu->intl_callback().set_inputline(m_main_cpu, 0, HOLD_LINE);

	/* sound hardware */
	SPEAKER(config, "speaker", 2).front();

	ym2610_device &ymsnd(YM2610(config, "ymsnd", 24000000/3));
	ymsnd.irq_handler().set_inputline(m_audio_cpu, 0);
	ymsnd.add_route(0, "speaker", 0.75, 0);
	ymsnd.add_route(0, "speaker", 0.75, 1);
	ymsnd.add_route(1, "speaker", 1.0, 0);
	ymsnd.add_route(2, "speaker", 1.0, 1);

	tc0140syt_device &tc0140syt(TC0140SYT(config, "tc0140syt", 0));
	tc0140syt.nmi_callback().set_inputline(m_audio_cpu, INPUT_LINE_NMI);
	tc0140syt.reset_callback().set_inputline(m_audio_cpu, INPUT_LINE_RESET);

	pc060ha_device &ciu(PC060HA(config, "ciu", 0));
	ciu.nmi_callback().set_inputline(m_audio_cpu, INPUT_LINE_NMI);
	ciu.reset_callback().set_inputline(m_audio_cpu, INPUT_LINE_RESET);
}

ROM_START( quakelof )
	ROM_REGION( 0x40000, "maincpu", 0 )
	ROM_LOAD( "c01-06.bin", 0x00000, 0x20000, CRC(90fa1409) SHA1(7904488d567ce5d8705b2d2c8a4b4aae310cc28b) )
	ROM_LOAD( "c01-04.rom", 0x20000, 0x20000, CRC(358bd076) SHA1(beb20a09370d05de719dde596eadca8fecb14ce5) )

	ROM_REGION( 0x10000, "audiocpu", 0 )      /* sound cpu */
	ROM_LOAD( "b82_10.ic5", 0x00000, 0x10000, CRC(a38aaaed) SHA1(d476ea516a797e71e0306da54c17ed1759fe1ccd) )

	ROM_REGION( 0x20000, "slave", 0 )
	ROM_LOAD( "c01-07.rom", 0x00000, 0x20000, CRC(5117c98f) SHA1(16b3a443eb113d2591833884a1b0ff297d8c00a4) )

	ROM_REGION( 0x200000, "tc0180vcu", 0 )
	ROM_LOAD( "c16-01.1", 0x000000, 0x080000, CRC(7059ce83) SHA1(1e6825ab944254cd4ba6574762172245b3352319) )
	ROM_LOAD( "c16-02.2", 0x080000, 0x080000, CRC(b458e905) SHA1(b712cbf4a4015e1fc2243871fe753e230f0172c2) )
	ROM_LOAD( "c16-03.3", 0x100000, 0x080000, CRC(515a9431) SHA1(836be28614326d093be8841617cca83cef8d55cc) )
	ROM_LOAD( "c16-04.4", 0x180000, 0x080000, CRC(ebf285e2) SHA1(0f806e42778e28e9687d85b2601ee08dd786869b) )

	ROM_REGION( 0x80000, "ymsnd:adpcma", 0 )   /* ADPCM samples */
	ROM_LOAD( "b82-02.ic1", 0x00000, 0x80000, CRC(5dd06bdd) SHA1(6eeaec6743805ba429b0ef58a530bc0740646324) )

	ROM_REGION( 0x80000, "ymsnd:adpcmb", 0 )    /* Delta-T samples */
	ROM_LOAD( "b82-01.ic2", 0x00000, 0x80000, CRC(f0eb6846) SHA1(4697c3fd61ac0d55c0d2a4354ff74719947397c5) )
ROM_END

} // anonymous namespace

GAME( 2012, quakelof,   0,        comet02_96b,   quakelof,   comet02_96b_state,     empty_init,     ROT0,   "Niokki Gamestudios", "Quake: The Labyrinth of Fear", 0 )