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

    Epson LX-800 dot matrix printer

    Skeleton driver

    - CPU type uPD7810HG
    - CPU PORTD and PORTF are connected to the Gate Array
    - CPU Analog Port (AN0-AN7) is not emulated (connects to DIPSW2 and
      some other things).
    - processing gets stuck in a loop, and never gets to scan the
      input buttons and switches.
    - CPU disassembly doesn't seem to indicate conditional JR or RET.

***************************************************************************/
#define ADDRESS_MAP_MODERN

#include "emu.h"
#include "cpu/upd7810/upd7810.h"
#include "machine/e05a03.h"
#include "sound/beep.h"
#include "lx800.lh"


/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

class lx800_state : public driver_device
{
public:
	lx800_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag),
	m_maincpu(*this, "maincpu"),
	m_beep(*this, BEEPER_TAG)
	{ }

	required_device<cpu_device> m_maincpu;
	required_device<device_t> m_beep;
	DECLARE_READ8_MEMBER(lx800_porta_r);
	DECLARE_WRITE8_MEMBER(lx800_porta_w);
	DECLARE_READ8_MEMBER(lx800_portc_r);
	DECLARE_WRITE8_MEMBER(lx800_portc_w);
	DECLARE_READ8_MEMBER(lx800_centronics_data_r);
	DECLARE_WRITE_LINE_MEMBER(lx800_centronics_pe_w);
	DECLARE_WRITE_LINE_MEMBER(lx800_paperempty_led_w);
	DECLARE_WRITE_LINE_MEMBER(lx800_reset_w);
};


/***************************************************************************
    I/O PORTS
***************************************************************************/

/* PA0   W  CRCOM  carriage motor, 0 = holding voltage, 1 = drive voltage
 * PA1             not used
 * PA2   W  PFCOM  paper feed motor, 0 = holding voltage, 1 = drive voltage
 * PA3  R   LF SW  line feed switch
 * PA4  R   FF SW  form feed switch
 * PA5  R   PE SW  paper end sensor, 0 = no paper, 1 = paper
 * PA6             not used
 * PA7  R   P/S    P/S signal from the optional interface
 */
READ8_MEMBER( lx800_state::lx800_porta_r )
{
	UINT8 result = 0;

	logerror("%s: lx800_porta_r(%02x)\n", machine().describe_context(), offset);

	result |= input_port_read(machine(), "LINEFEED") << 3;
	result |= input_port_read(machine(), "FORMFEED") << 4;
	result |= 1 << 5;

	result |= 1 << 7;

	return result;
}

WRITE8_MEMBER( lx800_state::lx800_porta_w )
{
	logerror("%s: lx800_porta_w(%02x): %02x\n", machine().describe_context(), offset, data);
	logerror("--> carriage: %d, paper feed: %d\n", BIT(data, 0), BIT(data, 2));
}

/* PC0   W  TXD        serial i/o txd
 * PC1  R   RXD        serial i/o rxd
 * PC2   W  ONLINE LP  online led
 * PC3  R   ONLINE SW  online switch
 * PC4   W  ERR        centronics error
 * PC5   W  ACK        centronics acknowledge
 * PC6   W  FIRE       drive pulse width signal
 * PC7   W  BUZZER     buzzer signal
 */
READ8_MEMBER( lx800_state::lx800_portc_r )
{
	UINT8 result = 0;

	logerror("%s: lx800_portc_r(%02x)\n", machine().describe_context(), offset);

	result |= input_port_read(machine(), "ONLINE") << 3;

	return result;
}

WRITE8_MEMBER( lx800_state::lx800_portc_w )
{
	logerror("%s: lx800_portc_w(%02x): %02x\n", machine().describe_context(), offset, data);
	logerror("--> err: %d, ack: %d, fire: %d, buzzer: %d\n", BIT(data, 4), BIT(data, 5), BIT(data, 6), BIT(data, 7));

	output_set_value("online_led", !BIT(data, 2));
	beep_set_state(m_beep, !BIT(data, 7));
}


/***************************************************************************
    GATE ARRAY
***************************************************************************/

READ8_MEMBER( lx800_state::lx800_centronics_data_r )
{
	logerror("centronics: data read\n");
	return 0x55;
}

WRITE_LINE_MEMBER( lx800_state::lx800_centronics_pe_w )
{
	logerror("centronics: pe = %d\n", state);
}

WRITE_LINE_MEMBER( lx800_state::lx800_paperempty_led_w )
{
	logerror("setting paperout led: %d\n", state);
	output_set_value("paperout_led", state);
}

WRITE_LINE_MEMBER( lx800_state::lx800_reset_w )
{
	logerror("cpu reset");
	m_maincpu->reset();
}


/***************************************************************************
    MACHINE EMULATION
***************************************************************************/

static MACHINE_START( lx800 )
{
	lx800_state *lx800 = machine.driver_data<lx800_state>();

	beep_set_state(lx800->m_beep, 0);
	beep_set_frequency(lx800->m_beep, 4000); /* ? */
}


/***************************************************************************
    ADDRESS MAPS
***************************************************************************/

static ADDRESS_MAP_START( lx800_mem, AS_PROGRAM, 8, lx800_state )
	AM_RANGE(0x0000, 0x7fff) AM_ROM /* 32k firmware */
	AM_RANGE(0x8000, 0x9fff) AM_RAM /* 8k external RAM */
	AM_RANGE(0xa000, 0xbfff) AM_NOP /* not used */
	AM_RANGE(0xc000, 0xdfff) AM_MIRROR(0x1ff8) AM_DEVREADWRITE_LEGACY("ic3b", e05a03_r, e05a03_w)
	AM_RANGE(0xe000, 0xfeff) AM_NOP /* not used */
	AM_RANGE(0xff00, 0xffff) AM_RAM /* internal CPU RAM */
ADDRESS_MAP_END

static ADDRESS_MAP_START( lx800_io, AS_IO, 8, lx800_state )
	AM_RANGE(UPD7810_PORTA, UPD7810_PORTA) AM_READWRITE(lx800_porta_r, lx800_porta_w)
	AM_RANGE(UPD7810_PORTB, UPD7810_PORTB) AM_READ_PORT("DIPSW1")
	AM_RANGE(UPD7810_PORTC, UPD7810_PORTC) AM_READWRITE(lx800_portc_r, lx800_portc_w)
ADDRESS_MAP_END


/***************************************************************************
    INPUT PORTS
***************************************************************************/

static INPUT_PORTS_START( lx800 )
	PORT_START("ONLINE")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("On Line") PORT_CODE(KEYCODE_O)

	PORT_START("FORMFEED")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Form Feed") PORT_CODE(KEYCODE_F)

	PORT_START("LINEFEED")
	PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CODE(KEYCODE_L)

	PORT_START("DIPSW1")
	PORT_DIPNAME(0x01, 0x00, "Typeface")
	PORT_DIPLOCATION("DIP:8")
	PORT_DIPSETTING(0x01, "Condensed")
	PORT_DIPSETTING(0x00, DEF_STR(Normal))
	PORT_DIPNAME(0x02, 0x00, "ZERO font")
	PORT_DIPLOCATION("DIP:7")
	PORT_DIPSETTING(0x02, "0")
	PORT_DIPSETTING(0x00, "0")
	PORT_DIPNAME(0x04, 0x00, "Character Table")
	PORT_DIPLOCATION("DIP:6")
	PORT_DIPSETTING(0x04, "Graphic")
	PORT_DIPSETTING(0x00, "Italic")
	PORT_DIPNAME(0x08, 0x00, "Paper-out detection")
	PORT_DIPLOCATION("DIP:5")
	PORT_DIPSETTING(0x08, "Valid")
	PORT_DIPSETTING(0x00, "Invalid")
	PORT_DIPNAME(0x10, 0x00, "Printing quality")
	PORT_DIPLOCATION("DIP:4")
	PORT_DIPSETTING(0x10, "NLQ")
	PORT_DIPSETTING(0x00, "Draft")
	PORT_DIPNAME(0xe0, 0xe0, "International character set")
	PORT_DIPLOCATION("DIP:3,2,1")
	PORT_DIPSETTING(0xe0, "U.S.A.")
	PORT_DIPSETTING(0x60, "France")
	PORT_DIPSETTING(0xa0, "Germany")
	PORT_DIPSETTING(0x20, "U.K.")
	PORT_DIPSETTING(0xc0, "Denmark")
	PORT_DIPSETTING(0x40, "Sweden")
	PORT_DIPSETTING(0x80, "Italy")
	PORT_DIPSETTING(0x00, "Spain")

	PORT_START("DIPSW2")
	PORT_DIPNAME(0x01, 0x00, "Page length")
	PORT_DIPLOCATION("DIP:4")
	PORT_DIPSETTING(0x01, "12\"")
	PORT_DIPSETTING(0x00, "11\"")
	PORT_DIPNAME(0x02, 0x00, "Cut sheet feeder mode")
	PORT_DIPLOCATION("DIP:3")
	PORT_DIPSETTING(0x02, "Valid")
	PORT_DIPSETTING(0x00, "Invalid")
	PORT_DIPNAME(0x04, 0x00, "1\" skip over perforation")
	PORT_DIPLOCATION("DIP:2")
	PORT_DIPSETTING(0x04, "Valid")
	PORT_DIPSETTING(0x00, "Invalid")
	PORT_DIPNAME(0x08, 0x00, "AUTO FEED XT control")
	PORT_DIPLOCATION("DIP:1")
	PORT_DIPSETTING(0x08, "Fix to LOW")
	PORT_DIPSETTING(0x00, "Depends on external signal")
INPUT_PORTS_END


/***************************************************************************
    MACHINE DRIVERS
***************************************************************************/

static const UPD7810_CONFIG lx800_cpu_config =
{
	TYPE_7810,
	0
};

static const e05a03_interface lx800_e05a03_intf =
{
	DEVCB_DRIVER_MEMBER(lx800_state, lx800_centronics_data_r),
	DEVCB_NULL,
	DEVCB_DRIVER_LINE_MEMBER(lx800_state, lx800_paperempty_led_w),
	DEVCB_DRIVER_LINE_MEMBER(lx800_state, lx800_centronics_pe_w),
	DEVCB_DRIVER_LINE_MEMBER(lx800_state, lx800_reset_w)
};

static MACHINE_CONFIG_START( lx800, lx800_state )
	/* basic machine hardware */
	MCFG_CPU_ADD("maincpu", UPD7810, XTAL_14_7456MHz)
	MCFG_CPU_CONFIG(lx800_cpu_config)
	MCFG_CPU_PROGRAM_MAP(lx800_mem)
	MCFG_CPU_IO_MAP(lx800_io)

	MCFG_MACHINE_START(lx800)

	MCFG_DEFAULT_LAYOUT(layout_lx800)

	/* audio hardware */
	MCFG_SPEAKER_STANDARD_MONO("mono")
	MCFG_SOUND_ADD(BEEPER_TAG, BEEP, 0)
	MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)

	/* gate array */
	MCFG_E05A03_ADD("ic3b", lx800_e05a03_intf)
MACHINE_CONFIG_END


/***************************************************************************
    ROM DEFINITIONS
***************************************************************************/

ROM_START( lx800 )
	ROM_REGION(0x8000, "maincpu", 0)
	ROM_LOAD("lx800.ic3c", 0x0000, 0x8000, CRC(da06c45b) SHA1(9618c940dd10d5b43cd1edd5763b90e6447de667))
ROM_END


/***************************************************************************
    GAME DRIVERS
***************************************************************************/

/*    YEAR  NAME   PARENT  COMPAT  MACHINE  INPUT  INIT  COMPANY  FULLNAME  FLAGS */
COMP( 1987, lx800, 0,      0,      lx800,   lx800, 0,    "Epson", "LX-800 Printer", GAME_NOT_WORKING )
