#include "emu.h"
#include "cpu/m6809/m6809.h"
#include "machine/6522via.h"
#include "includes/beezer.h"


static READ8_DEVICE_HANDLER( b_via_0_pa_r );
static READ8_DEVICE_HANDLER( b_via_0_pb_r );
static WRITE8_DEVICE_HANDLER( b_via_0_pa_w );
static WRITE8_DEVICE_HANDLER( b_via_0_pb_w );
static READ_LINE_DEVICE_HANDLER( b_via_0_ca2_r );

static READ8_DEVICE_HANDLER( b_via_1_pa_r );
static READ8_DEVICE_HANDLER( b_via_1_pb_r );
static WRITE8_DEVICE_HANDLER( b_via_1_pa_w );
static WRITE8_DEVICE_HANDLER( b_via_1_pb_w );


/* VIA 0 (aka "PPCNP74", U6 @1C on schematics)
    enabled at CE00-CFFF of main m6809 cpu when bankswitch is set to 0
    port A:
        bit 7: input, X from banking latch (d3 of banking register)
        bit 6: input, Y from banking latch (d4 of banking register)
        bit 5: input, Z from banking latch (d5 of banking register)
        bit 4: N/C
        bit 3: output, /RESET for audio subcpu
        bit 2: output, /ENABLE for LS139@2H for reading control and dipswitch inputs to pbus
        bit 1: output, MSb of selector for inputs to pbus
        bit 0: output, LSb of "
    port B:
        bits 7-0: input/output: pbus
    port C:
        CA1: N/C
        CA2: input: "TDISP" (one of the higher bits in the video line counter, a mirror of the D5 bit from beezer_line_r), done in /video/beezer.c
        CB1: ASH1 to via 1
        CB2: ASH2 to via 1
    /IRQ: to main m6809 cpu
    /RES: from main reset generator/watchdog/button

    TODO: find a better way to attach ca2 read to beezer_line_r
    */
const via6522_interface b_via_0_interface =
{
	/*inputs : A/B         */ DEVCB_HANDLER(b_via_0_pa_r), DEVCB_HANDLER(b_via_0_pb_r),
	/*inputs : CA/B1,CA/B2 */ DEVCB_NULL, DEVCB_DEVICE_LINE_MEMBER("via6522_1", via6522_device, read_ca2), DEVCB_LINE(b_via_0_ca2_r), DEVCB_DEVICE_LINE_MEMBER("via6522_1", via6522_device, read_ca1),
	/*outputs: A/B         */ DEVCB_HANDLER(b_via_0_pa_w), DEVCB_HANDLER(b_via_0_pb_w),
	/*outputs: CA/B1,CA/B2 */ DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_DEVICE_LINE_MEMBER("via6522_1", via6522_device, write_ca1),
	/*irq                  */ DEVCB_CPU_INPUT_LINE("maincpu", M6809_IRQ_LINE)
};

/* VIA 1 (U18 @3C on schematics)
    port A:
        bits 7-0: input/output: pbus
    port B:
        bit 7: output: TIMER1 OUT (used to gate NOISE (see below) to clock channel 1 of 6840, plus acts as channel 0 by itself)
        bit 6: input: NOISE (from mm5837 14-bit LFSR, which also connects to clock above)
        bit 5: output?: N/C
        bit 4: output?: FMSEL1 (does not appear elsewhere on schematics! what does this do? needs tracing) - always 0?
        bit 3: output?: FMSEL0 (does not appear elsewhere on schematics! what does this do? needs tracing) - always 0?
        bit 2: output?: AM (does not appear elsewhere on schematics! what does this do? needs tracing) - always 0?
        bit 1: output: FM or AM (appears to control some sort of suppression or filtering change of the post-DAC amplifier when enabled, only during the TIMER1 OUT time-slot of the multiplexer, see page 1B 3-3 of schematics) - always 0? why is there a special circuit for it?
        bit 0: output?: DMOD DISABLE (does not appear elsewhere on schematics! what does this do? needs tracing) - on startup is 0, turns to 1 and stays that way?
    port C:
        CA1: AHS2 from via 0 (are these two switched?)
        CA2: AHS1 from via 0 "
        CB1: ??put: DMOD CLR (does not appear elsewhere on schematics! what does this do? needs tracing)
        CB2: ??put: DMOD DATA (does not appear elsewhere on schematics! what does this do? needs tracing)
    /IRQ: to audio/sub m6809 cpu
    /RES: from audio reset bit of via 0

    TODO: the entirety of port B, much needs tracing
    TODO: ports CB1 and CB2, need tracing; ports CA1 and CA2 could use verify as well
    */
const via6522_interface b_via_1_interface =
{
	/*inputs : A/B         */ DEVCB_HANDLER(b_via_1_pa_r), DEVCB_HANDLER(b_via_1_pb_r),
	/*inputs : CA/B1,CA/B2 */ DEVCB_DEVICE_LINE_MEMBER("via6522_0", via6522_device, read_cb2), DEVCB_NULL, DEVCB_DEVICE_LINE_MEMBER("via6522_0", via6522_device, read_cb1), DEVCB_NULL,
	/*outputs: A/B         */ DEVCB_HANDLER(b_via_1_pa_w), DEVCB_HANDLER(b_via_1_pb_w),
	/*outputs: CA/B1,CA/B2 */ DEVCB_NULL, DEVCB_NULL, DEVCB_DEVICE_LINE_MEMBER("via6522_0", via6522_device, write_cb1), DEVCB_NULL,
	/*irq                  */ DEVCB_CPU_INPUT_LINE("audiocpu", M6809_IRQ_LINE)
};

static READ_LINE_DEVICE_HANDLER( b_via_0_ca2_r )
{
	return 0; // TODO: TDISP on schematic, same as D5 bit of scanline count from 74LS161 counter at 7A; attach properly

}

static READ8_DEVICE_HANDLER( b_via_0_pa_r )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	return (state->m_banklatch&0x38)<<2; // return X,Y,Z bits TODO: the Z bit connects somewhere else... where?
}

static READ8_DEVICE_HANDLER( b_via_0_pb_r )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	return state->m_pbus;
}

static WRITE8_DEVICE_HANDLER( b_via_0_pa_w )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	if ((data & 0x08) == 0)
		cputag_set_input_line(device->machine(), "audiocpu", INPUT_LINE_RESET, ASSERT_LINE);
	else
		cputag_set_input_line(device->machine(), "audiocpu", INPUT_LINE_RESET, CLEAR_LINE);

	if ((data & 0x04) == 0)
	{
		switch (data & 0x03)
		{
		case 0:
			state->m_pbus = input_port_read(device->machine(), "IN0");
			break;
		case 1:
			state->m_pbus = input_port_read(device->machine(), "IN1") | (input_port_read(device->machine(), "IN2") << 4);
			break;
		case 2:
			state->m_pbus = input_port_read(device->machine(), "DSWB");
			break;
		case 3:
			state->m_pbus = input_port_read(device->machine(), "DSWA"); // Technically DSWA isn't populated on the board and is pulled to 0xFF with resistor pack, but there IS a DSWA port in the driver so we may as well use it.
			break;
		}
	}
}

static WRITE8_DEVICE_HANDLER( b_via_0_pb_w )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	state->m_pbus = data;
}

static READ8_DEVICE_HANDLER( b_via_1_pa_r )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	return state->m_pbus;
}

static READ8_DEVICE_HANDLER( b_via_1_pb_r )
{
	return 0x1F | (beezer_noise_r(device->machine().device("custom"), 0)?0x40:0);
}

static WRITE8_DEVICE_HANDLER( b_via_1_pa_w )
{
	beezer_state *state = device->machine().driver_data<beezer_state>();
	state->m_pbus = data;
}

static WRITE8_DEVICE_HANDLER( b_via_1_pb_w )
{
	beezer_timer1_w(device->machine().device("custom"), 0, data&0x80);
	//if ((data&0x1f) != 0x01)
	//  popmessage("via1 pb low write of 0x%02x is not supported! contact mamedev!", data&0x1f);
}

DRIVER_INIT( beezer )
{
	beezer_state *state = machine.driver_data<beezer_state>();
	state->m_pbus = 0;
	state->m_banklatch = 0;
}

WRITE8_HANDLER( beezer_bankswitch_w )
{
	beezer_state *state = space->machine().driver_data<beezer_state>();
	state->m_banklatch = data&0x3f; // latched 'x,y,z' plus bank bits in ls174 @ 4H
	if ((data & 0x07) == 0)
	{
		via6522_device *via_0 = space->machine().device<via6522_device>("via6522_0");
		space->install_legacy_write_handler(0xc600, 0xc7ff, FUNC(watchdog_reset_w));
		space->install_legacy_write_handler(0xc800, 0xc9ff, FUNC(beezer_map_w));
		space->install_legacy_read_handler(0xca00, 0xcbff, FUNC(beezer_line_r));
		space->install_readwrite_handler(0xce00, 0xcfff, read8_delegate(FUNC(via6522_device::read), via_0), write8_delegate(FUNC(via6522_device::write), via_0));
	}
	else
	{
		UINT8 *rom = space->machine().region("maincpu")->base() + 0x10000;
		space->install_ram(0xc000, 0xcfff, rom + (data & 0x07) * 0x2000 + ((data & 0x08) ? 0x1000: 0));
	}
}
